-Embedded libraries
-==================
-
-The source package embeds a copy of libgit2 0.25.1 because that is what this
-version of cargo needs, but the Debian libgit2 0.25.1 package cannot yet go
-into unstable due to the testing freeze. It will be removed after the freeze.
-
Updating the package
====================
bash-completion,
libcurl4-gnutls-dev | libcurl4-openssl-dev,
libssh2-1-dev,
-# TODO: re-enable after the freeze
-# libgit2-dev (>= 0.25.1),
+ libgit2-dev (>= 0.25.1),
libhttp-parser-dev,
libssl-dev,
zlib1g-dev,
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
-
-# TODO: Everything below here can be removed after the freeze
-
-Files: debian/libgit2/*
-Copyright: 2009-2012, the libgit2 contributors
-License: GPL-2 with linking exception, origin admission
-
-Files: debian/libgit2/cmake/Modules/FindGSSAPI.cmake
-Copyright: 2013, Andreas Schneider <asn@cryptomilk.org>
-License: BSD-2-clause
-
-Files: debian/libgit2/include/git2/inttypes.h debian/libgit2/include/git2/stdint.h
-Copyright: 2006, Alexander Chemeris
-License: BSD-3-clause-modified
-
-Files: debian/libgit2/src/khash.h
-Copyright: 2008, 2009, 2011, Attractive Chaos <attractor@live.co.uk>
-License: MIT-License
-
-Files: debian/libgit2/src/tsort.c
-Copyright: 2010, Christopher Swenson
- 2011, Vicent Marti
-License: MIT-License
-
-Files: debian/libgit2/src/path.c debian/libgit2/src/fnmatch.h
-Copyright: 2008, The Android Open Source Project
-License: BSD-2-clause
-
-Files: debian/libgit2/src/util.c
-Copyright: 2009, Public Software Group e. V., Berlin, Germany
- 1990, Regents of the University of California.
-License: MIT-License and BSD-3-clause
-
-Files: debian/libgit2/src/xdiff/*
-Copyright: 2003, Davide Libenzi
-License: LGPL-2.1+
-
-Files: debian/libgit2/src/xdiff/xmerge.c
-Copyright: 2003-2006, Davide Libenzi
- 2003-2006, Johannes E. Schindelin
-License: LGPL-2.1+
-
-Files: debian/libgit2/src/xdiff/xpatience.c
-Copyright: 2003-2009, Davide Libenzi
- 2003-2009, Johannes E. Schindelin
-License: LGPL-2.1+
-
-Files: debian/libgit2/src/xdiff/xhistogram.c
-Copyright: 2010, Google Inc and others from JGit's IP log.
-License: EDL-1.0
-
-Files: debian/libgit2/src/win32/posix_w32.c
-Copyright: 1999 - 2012, The PHP Group
-License: PHP-3.01
-
-Files: debian/libgit2/src/fnmatch.c
-Copyright: 1989, 1993, 1994, The Regents of the University of California.
-License: BSD-3-clause
-
-Files: debian/libgit2/src/date.c
-Copyright: 2005, Linus Torvalds
-License: GPL-2 with linking exception
- .
- Note that the only valid version of the GPL as far as this project
- is concerned is _this_ particular version of the license (ie v2, not
- v2.2 or v3.x or whatever), unless explicitly otherwise stated.
- .
- LINKING EXCEPTION
- .
- In addition to the permissions in the GNU General Public License,
- the authors give you unlimited permission to link the compiled
- version of this library into combinations with other programs,
- and to distribute those combinations without any restriction
- coming from the use of this file. (The General Public License
- restrictions do apply in other respects; for example, they cover
- modification of the file, and distribution when not linked into
- a combined executable.)
- .
- On Debian systems, the complete text of the GNU General
- Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
-
-License: GPL-2 with linking exception, origin admission
- libgit2 is Copyright (C) 2009-2011 the libgit2 contributors,
- unless otherwise stated. See the AUTHORS file for details.
- .
- Note that the only valid version of the GPL as far as this project
- is concerned is _this_ particular version of the license (ie v2, not
- v2.2 or v3.x or whatever), unless explicitly otherwise stated.
- .
- LINKING EXCEPTION
- .
- In addition to the permissions in the GNU General Public License,
- the authors give you unlimited permission to link the compiled
- version of this library into combinations with other programs,
- and to distribute those combinations without any restriction
- coming from the use of this file. (The General Public License
- restrictions do apply in other respects; for example, they cover
- modification of the file, and distribution when not linked into
- a combined executable.)
- .
- On Debian systems, the complete text of the GNU General
- Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
- .
- ----------------------------------------------------------------------
- .
- The priority queue implementation is based on code licensed under the
- Apache 2.0 license:
- .
- Copyright 2010 Volkan Yazıcı <volkan.yazici@gmail.com>
- Copyright 2006-2010 The Apache Software Foundation
- .
- The full text of the Apache 2.0 license is available at:
- .
- http://www.apache.org/licenses/LICENSE-2.0
-
-License: BSD-3-clause
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. [rescinded 22 July 1999]
- 4. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
- .
- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
-License: BSD-3-clause-modified
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- .
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- .
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- .
- 3. The name of the author may be used to endorse or promote products
- derived from this software without specific prior written permission.
- .
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-License: LGPL-2.1+
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- .
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- .
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301 USA.
-
-License: EDL-1.0
- This program and the accompanying materials are made available
- under the terms of the Eclipse Distribution License v1.0 which
- accompanies this distribution, is reproduced below, and is
- available at http://www.eclipse.org/org/documents/edl-v10.php
- .
- All rights reserved.
- .
- Redistribution and use in source and binary forms, with or
- without modification, are permitted provided that the following
- conditions are met:
- .
- - Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- .
- - Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
- .
- - Neither the name of the Eclipse Foundation, Inc. nor the
- names of its contributors may be used to endorse or promote
- products derived from this software without specific prior
- written permission.
- .
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-License: PHP-3.01
- Redistribution and use in source and binary forms, with or without
- modification, is permitted provided that the following conditions
- are met:
- .
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- .
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
- .
- 3. The name "PHP" must not be used to endorse or promote products
- derived from this software without prior written permission. For
- written permission, please contact group@php.net.
- .
- 4. Products derived from this software may not be called "PHP", nor
- may "PHP" appear in their name, without prior written permission
- from group@php.net. You may indicate that your software works in
- conjunction with PHP by saying "Foo for PHP" instead of calling
- it "PHP Foo" or "phpfoo"
- .
- 5. The PHP Group may publish revised and/or new versions of the
- license from time to time. Each version will be given a
- distinguishing version number.
- Once covered code has been published under a particular version
- of the license, you may always continue to use it under the terms
- of that version. You may also choose to use such covered code
- under the terms of any subsequent version of the license
- published by the PHP Group. No one other than the PHP Group has
- the right to modify the terms applicable to covered code created
- under this License.
- .
- 6. Redistributions of any form whatsoever must retain the following
- acknowledgment:
- "This product includes PHP software, freely available from
- <http://www.php.net/software/>".
- .
- THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
- ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
- DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- OF THE POSSIBILITY OF SUCH DAMAGE.
+++ /dev/null
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
+++ /dev/null
-; Check http://editorconfig.org/ for more informations
-; Top-most EditorConfig file
-root = true
-
-; tab indentation
-[*]
-indent_style = tab
-trim_trailing_whitespace = true
-insert_final_newline = true
-
-; 4-column space indentation
-[*.md]
-indent_style = space
-indent_size = 4
+++ /dev/null
-You are opening a _bug report_ against the libgit2 project. If you have a
-question about an API or usage, please ask on StackOverflow:
-http://stackoverflow.com/questions/tagged/libgit2. Please fill out the
-reproduction steps (below) and delete this introductory paragraph. Thanks!
-
-### Reproduction steps
-
-### Expected behavior
-
-### Actual behavior
-
-### Version of libgit2 (release number or SHA1)
-
-### Operating system(s) tested
+++ /dev/null
-/tests/clar.suite
-/tests/clar.suite.rule
-/tests/.clarcache
-/apidocs
-/trash-*.exe
-/libgit2.pc
-/config.mak
-*.o
-*.a
-*.exe
-*.gcda
-*.gcno
-*.gcov
-.lock-wafbuild
-.waf*
-build/
-build-amiga/
-tests/tmp/
-msvc/Debug/
-msvc/Release/
-*.sln
-*.suo
-*.vc*proj*
-*.sdf
-*.opensdf
-*.aps
-*.cmake
-!cmake/Modules/*.cmake
-.DS_Store
-*~
-.*.swp
-tags
-mkmf.log
+++ /dev/null
-Vicent Martà <vicent@github.com> Vicent Marti <tanoku@gmail.com>
-Vicent Martà <vicent@github.com> Vicent Martà <tanoku@gmail.com>
-Michael Schubert <schu@schu.io> schu <schu-github@schulog.org>
-Ben Straub <bs@github.com> Ben Straub <ben@straubnet.net>
-Ben Straub <bs@github.com> Ben Straub <bstraub@github.com>
-Carlos MartÃn Nieto <cmn@dwim.me> <carlos@cmartin.tk>
-Carlos MartÃn Nieto <cmn@dwim.me> <cmn@elego.de>
-nulltoken <emeric.fermas@gmail.com> <emeric.fermas@gmail.com>
-Scott J. Goldman <scottjg@github.com> <scottjgo@gmail.com>
-Martin Woodward <martin.woodward@microsoft.com> <martinwo@microsoft.com>
-Peter Drahoš <drahosp@gmail.com> <drahosp@gmail.com>
-Adam Roben <adam@github.com> <adam@roben.org>
-Adam Roben <adam@github.com> <adam@github.com>
-Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.ca>
-Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.tk>
-Sascha Cunz <sascha@babbelbox.org> <Sascha@BabbelBox.org>
-Authmillenon <authmillenon@googlemail.com> <martin@ucsmail.de>
-Authmillenon <authmillenon@googlemail.com> <authmillenon@googlemail.com>
-Edward Thomson <ethomson@github.com> <ethomson@microsoft.com>
-Edward Thomson <ethomson@github.com> <ethomson@edwardthomson.com>
-J. David Ibáñez <jdavid.ibp@gmail.com> <jdavid@itaapy.com>
-Russell Belfer <rb@github.com> <arrbee@arrbee.com>
+++ /dev/null
-# Travis-CI Build for libgit2
-# see travis-ci.org for details
-
-language: c
-
-os:
- - linux
- - osx
-
-compiler:
- - gcc
- - clang
-
-# Settings to try
-env:
- global:
- - secure: "YnhS+8n6B+uoyaYfaJ3Lei7cSJqHDPiKJCKFIF2c87YDfmCvAJke8QtE7IzjYDs7UFkTCM4ox+ph2bERUrxZbSCyEkHdjIZpKuMJfYWja/jgMqTMxdyOH9y8JLFbZsSXDIXDwqBlC6vVyl1fP90M35wuWcNTs6tctfVWVofEFbs="
- - GITTEST_INVASIVE_FS_SIZE=1
- matrix:
- - OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release"
- - OPTIONS="-DTHREADSAFE=OFF -DBUILD_EXAMPLES=ON"
-
-addons:
- apt:
- packages:
- - cmake
- - libssh2-1-dev
- - openssh-client
- - openssh-server
- - valgrind
-
-sudo: false
-
-matrix:
- fast_finish: true
- exclude:
- - os: osx
- compiler: gcc
- include:
- - compiler: gcc
- env: COVERITY=1
- os: linux
- - compiler: gcc
- env:
- - VALGRIND=1
- OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug"
- os: linux
- allow_failures:
- - env: COVERITY=1
-
-install:
- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi
-
-# Run the Build script and tests
-script:
- - script/cibuild.sh
-
-# Run Tests
-after_success:
- - if [ "$TRAVIS_OS_NAME" = "linux" -a -n "$VALGRIND" ]; then valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline; fi
-
-# Only watch the development and master branches
-branches:
- only:
- - master
- - /^maint.*/
-
-# Notify development list when needed
-notifications:
- irc:
- channels:
- - irc.freenode.net#libgit2
- on_success: change
- on_failure: always
- use_notice: true
- skip_join: true
- campfire:
- on_success: always
- on_failure: always
- rooms:
- - secure: "sH0dpPWMirbEe7AvLddZ2yOp8rzHalGmv0bYL/LIhVw3JDI589HCYckeLMSB\n3e/FeXw4bn0EqXWEXijVa4ijbilVY6d8oprdqMdWHEodng4KvY5vID3iZSGT\nxylhahO1XHmRynKQLOAvxlc93IlpVW38vQfby8giIY1nkpspb2w="
+++ /dev/null
-The following people contribute or have contributed
-to the libgit2 project (sorted alphabetically):
-
-Alex Budovski
-Alexei Sholik
-Andreas Ericsson
-Anton "antong" Gyllenberg
-Ankur Sethi
-Arthur Schreiber
-Ben Noordhuis
-Ben Straub
-Benjamin C Meyer
-Brian Downing
-Brian Lopez
-Carlos MartÃn Nieto
-Colin Timmermans
-Daniel Huckstep
-Dave Borowitz
-David Boyce
-David Glesser
-Dmitry Kakurin
-Dmitry Kovega
-Emeric Fermas
-Emmanuel Rodriguez
-Florian Forster
-Holger Weiss
-Ingmar Vanhassel
-J. David Ibáñez
-Jacques Germishuys
-Jakob Pfender
-Jason Penny
-Jason R. McNeil
-Jerome Lambourg
-Johan 't Hart
-John Wiegley
-Jonathan "Duke" Leto
-Julien Miotte
-Julio Espinoza-Sokal
-Justin Love
-Kelly "kelly.leahy" Leahy
-Kirill A. Shutemov
-Lambert CLARA
-Luc Bertrand
-Marc Pegon
-Marcel Groothuis
-Marco Villegas
-Michael "schu" Schubert
-Microsoft Corporation
-Olivier Ramonat
-Peter Drahoš
-Pierre Habouzit
-Pierre-Olivier Latour
-Przemyslaw Pawelczyk
-Ramsay Jones
-Robert G. Jakabosky
-Romain Geissler
-Romain Muller
-Russell Belfer
-Sakari Jokinen
-Samuel Charles "Sam" Day
-Sarath Lakshman
-Sascha Cunz
-Sascha Peilicke
-Scott Chacon
-Sebastian Schuberth
-Sergey Nikishin
-Shawn O. Pearce
-Shuhei Tanuma
-Steve Frécinaux
-Sven Strickroth
-Tim Branyen
-Tim Clem
-Tim Harder
-Torsten Bögershausen
-Trent Mick
-Vicent Marti
+++ /dev/null
-v0.25 + 1
--------
-
-### Changes or improvements
-
-### API additions
-
-### API removals
-
-### Breaking API changes
-
-v0.25
--------
-
-### Changes or improvements
-
-* Fix repository discovery with `git_repository_discover` and
- `git_repository_open_ext` to match git's handling of a ceiling
- directory at the current directory. git only checks ceiling
- directories when its search ascends to a parent directory. A ceiling
- directory matching the starting directory will not prevent git from
- finding a repository in the starting directory or a parent directory.
-
-* Do not fail when deleting remotes in the presence of broken
- global configs which contain branches.
-
-* Support for reading and writing git index v4 files
-
-* Improve the performance of the revwalk and bring us closer to git's code.
-
-* The reference db has improved support for concurrency and returns `GIT_ELOCKED`
- when an operation could not be performed due to locking.
-
-* Nanosecond resolution is now activated by default, following git's change to
- do this.
-
-* We now restrict the set of ciphers we let OpenSSL use by default.
-
-* Users can now register their own merge drivers for use with `.gitattributes`.
- The library also gained built-in support for the union merge driver.
-
-* The default for creating references is now to validate that the object does
- exist.
-
-* Add `git_proxy_options` which is used by the different networking
- implementations to let the caller specify the proxy settings instead of
- relying on the environment variables.
-
-### API additions
-
-* You can now get the user-agent used by libgit2 using the
- `GIT_OPT_GET_USER_AGENT` option with `git_libgit2_opts()`.
- It is the counterpart to `GIT_OPT_SET_USER_AGENT`.
-
-* The `GIT_OPT_SET_SSL_CIPHERS` option for `git_libgit2_opts()` lets you specify
- a custom list of ciphers to use for OpenSSL.
-
-* `git_commit_create_buffer()` creates a commit and writes it into a
- user-provided buffer instead of writing it into the object db. Combine it with
- `git_commit_create_with_signature()` in order to create a commit with a
- cryptographic signature.
-
-* `git_blob_create_fromstream()` and
- `git_blob_create_fromstream_commit()` allow you to create a blob by
- writing into a stream. Useful when you do not know the final size or
- want to copy the contents from another stream.
-
-* New flags for `git_repository_open_ext`:
-
- * `GIT_REPOSITORY_OPEN_NO_DOTGIT` - Do not check for a repository by
- appending `/.git` to the `start_path`; only open the repository if
- `start_path` itself points to the git directory.
- * `GIT_REPOSITORY_OPEN_FROM_ENV` - Find and open a git repository,
- respecting the environment variables used by the git command-line
- tools. If set, `git_repository_open_ext` will ignore the other
- flags and the `ceiling_dirs` argument, and will allow a NULL
- `path` to use `GIT_DIR` or search from the current directory. The
- search for a repository will respect `$GIT_CEILING_DIRECTORIES`
- and `$GIT_DISCOVERY_ACROSS_FILESYSTEM`. The opened repository
- will respect `$GIT_INDEX_FILE`, `$GIT_NAMESPACE`,
- `$GIT_OBJECT_DIRECTORY`, and `$GIT_ALTERNATE_OBJECT_DIRECTORIES`.
- In the future, this flag will also cause `git_repository_open_ext`
- to respect `$GIT_WORK_TREE` and `$GIT_COMMON_DIR`; currently,
- `git_repository_open_ext` with this flag will error out if either
- `$GIT_WORK_TREE` or `$GIT_COMMON_DIR` is set.
-
-* `git_diff_from_buffer()` can create a `git_diff` object from the contents
- of a git-style patch file.
-
-* `git_index_version()` and `git_index_set_version()` to get and set
- the index version
-
-* `git_odb_expand_ids()` lets you check for the existence of multiple
- objects at once.
-
-* The new `git_blob_dup()`, `git_commit_dup()`, `git_tag_dup()` and
- `git_tree_dup()` functions provide type-specific wrappers for
- `git_object_dup()` to reduce noise and increase type safety for callers.
-
-* `git_reference_dup()` lets you duplicate a reference to aid in ownership
- management and cleanup.
-
-* `git_signature_from_buffer()` lets you create a signature from a string in the
- format that appear in objects.
-
-* `git_tree_create_updated()` lets you create a tree based on another one
- together with a list of updates. For the covered update cases, it's more
- efficient than the `git_index` route.
-
-* `git_apply_patch()` applies hunks from a `git_patch` to a buffer.
-
-* `git_diff_to_buf()` lets you print an entire diff directory to a buffer,
- similar to how `git_patch_to_buf()` works.
-
-* `git_proxy_init_options()` is added to initialize a `git_proxy_options`
- structure at run-time.
-
-* `git_merge_driver_register()`, `git_merge_driver_unregister()` let you
- register and unregister a custom merge driver to be used when `.gitattributes`
- specifies it.
-
-* `git_merge_driver_lookup()` can be used to look up a merge driver by name.
-
-* `git_merge_driver_source_repo()`, `git_merge_driver_source_ancestor()`,
- `git_merge_driver_source_ours()`, `git_merge_driver_source_theirs()`,
- `git_merge_driver_source_file_options()` added as accessors to
- `git_merge_driver_source`.
-
-### API removals
-
-* `git_blob_create_fromchunks()` has been removed in favour of
- `git_blob_create_fromstream()`.
-
-### Breaking API changes
-
-* `git_packbuilder_object_count` and `git_packbuilder_written` now
- return a `size_t` instead of a `uint32_t` for more thorough
- compatibility with the rest of the library.
-
-* `git_packbuiler_progress` now provides explicitly sized `uint32_t`
- values instead of `unsigned int`.
-
-* `git_diff_file` now includes an `id_abbrev` field that reflects the
- number of nibbles set in the `id` field.
-
-* `git_odb_backend` now has a `freshen` function pointer. This optional
- function pointer is similar to the `exists` function, but it will update
- a last-used marker. For filesystem-based object databases, this updates
- the timestamp of the file containing the object, to indicate "freshness".
- If this is `NULL`, then it will not be called and the `exists` function
- will be used instead.
-
-* `git_remote_connect()` now accepts proxy options.
-
-v0.24
--------
-
-### Changes or improvements
-
-* Custom merge drivers can now be registered, which allows callers to
- configure callbacks to honor `merge=driver` configuration in
- `.gitattributes`.
-
-* Custom filters can now be registered with wildcard attributes, for
- example `filter=*`. Consumers should examine the attributes parameter
- of the `check` function for details.
-
-* Symlinks are now followed when locking a file, which can be
- necessary when multiple worktrees share a base repository.
-
-* You can now set your own user-agent to be sent for HTTP requests by
- using the `GIT_OPT_SET_USER_AGENT` with `git_libgit2_opts()`.
-
-* You can set custom HTTP header fields to be sent along with requests
- by passing them in the fetch and push options.
-
-* Tree objects are now assumed to be sorted. If a tree is not
- correctly formed, it will give bad results. This is the git approach
- and cuts a significant amount of time when reading the trees.
-
-* Filter registration is now protected against concurrent
- registration.
-
-* Filenames which are not valid on Windows in an index no longer cause
- to fail to parse it on that OS.
-
-* Rebases can now be performed purely in-memory, without touching the
- repository's workdir.
-
-* When adding objects to the index, or when creating new tree or commit
- objects, the inputs are validated to ensure that the dependent objects
- exist and are of the correct type. This object validation can be
- disabled with the GIT_OPT_ENABLE_STRICT_OBJECT_CREATION option.
-
-* The WinHTTP transport's handling of bad credentials now behaves like
- the others, asking for credentials again.
-
-### API additions
-
-* `git_config_lock()` has been added, which allow for
- transactional/atomic complex updates to the configuration, removing
- the opportunity for concurrent operations and not committing any
- changes until the unlock.
-
-* `git_diff_options` added a new callback `progress_cb` to report on the
- progress of the diff as files are being compared. The documentation of
- the existing callback `notify_cb` was updated to reflect that it only
- gets called when new deltas are added to the diff.
-
-* `git_fetch_options` and `git_push_options` have gained a `custom_headers`
- field to set the extra HTTP header fields to send.
-
-* `git_stream_register_tls()` lets you register a callback to be used
- as the constructor for a TLS stream instead of the libgit2 built-in
- one.
-
-* `git_commit_header_field()` allows you to look up a specific header
- field in a commit.
-
-* `git_commit_extract_signature()` extracts the signature from a
- commit and gives you both the signature and the signed data so you
- can verify it.
-
-### API removals
-
-* No APIs were removed in this version.
-
-### Breaking API changes
-
-* `git_merge_options` now provides a `default_driver` that can be used
- to provide the name of a merge driver to be used to handle files changed
- during a merge.
-
-* The `git_merge_tree_flag_t` is now `git_merge_flag_t`. Subsequently,
- its members are no longer prefixed with `GIT_MERGE_TREE_FLAG` but are
- now prefixed with `GIT_MERGE_FLAG`, and the `tree_flags` field of the
- `git_merge_options` structure is now named `flags`.
-
-* The `git_merge_file_flags_t` enum is now `git_merge_file_flag_t` for
- consistency with other enum type names.
-
-* `git_cert` descendent types now have a proper `parent` member
-
-* It is the responsibility of the refdb backend to decide what to do
- with the reflog on ref deletion. The file-based backend must delete
- it, a database-backed one may wish to archive it.
-
-* `git_config_backend` has gained two entries. `lock` and `unlock`
- with which to implement the transactional/atomic semantics for the
- configuration backend.
-
-* `git_index_add` and `git_index_conflict_add()` will now use the case
- as provided by the caller on case insensitive systems. Previous
- versions would keep the case as it existed in the index. This does
- not affect the higher-level `git_index_add_bypath` or
- `git_index_add_frombuffer` functions.
-
-* The `notify_payload` field of `git_diff_options` was renamed to `payload`
- to reflect that it's also the payload for the new progress callback.
-
-* The `git_config_level_t` enum has gained a higher-priority value
- `GIT_CONFIG_LEVEL_PROGRAMDATA` which represent a rough Windows equivalent
- to the system level configuration.
-
-* `git_rebase_options` now has a `merge_options` field.
-
-* The index no longer performs locking itself. This is not something
- users of the library should have been relying on as it's not part of
- the concurrency guarantees.
-
-* `git_remote_connect()` now takes a `custom_headers` argument to set
- the extra HTTP header fields to send.
-
-v0.23
-------
-
-### Changes or improvements
-
-* Patience and minimal diff drivers can now be used for merges.
-
-* Merges can now ignore whitespace changes.
-
-* Updated binary identification in CRLF filtering to avoid false positives in
- UTF-8 files.
-
-* Rename and copy detection is enabled for small files.
-
-* Checkout can now handle an initial checkout of a repository, making
- `GIT_CHECKOUT_SAFE_CREATE` unnecessary for users of clone.
-
-* The signature parameter in the ref-modifying functions has been
- removed. Use `git_repository_set_ident()` and
- `git_repository_ident()` to override the signature to be used.
-
-* The local transport now auto-scales the number of threads to use
- when creating the packfile instead of sticking to one.
-
-* Reference renaming now uses the right id for the old value.
-
-* The annotated version of branch creation, HEAD detaching and reset
- allow for specifying the expression from the user to be put into the
- reflog.
-
-* `git_rebase_commit` now returns `GIT_EUNMERGED` when you attempt to
- commit with unstaged changes.
-
-* On Mac OS X, we now use SecureTransport to provide the cryptographic
- support for HTTPS connections insead of OpenSSL.
-
-* Checkout can now accept an index for the baseline computations via the
- `baseline_index` member.
-
-* The configuration for fetching is no longer stored inside the
- `git_remote` struct but has been moved to a `git_fetch_options`. The
- remote functions now take these options or the callbacks instead of
- setting them beforehand.
-
-* `git_submodule` instances are no longer cached or shared across
- lookup. Each submodule represents the configuration at the time of
- loading.
-
-* The index now uses diffs for `add_all()` and `update_all()` which
- gives it a speed boost and closer semantics to git.
-
-* The ssh transport now reports the stderr output from the server as
- the error message, which allows you to get the "repository not
- found" messages.
-
-* `git_index_conflict_add()` will remove staged entries that exist for
- conflicted paths.
-
-* The flags for a `git_diff_file` will now have the `GIT_DIFF_FLAG_EXISTS`
- bit set when a file exists on that side of the diff. This is useful
- for understanding whether a side of the diff exists in the presence of
- a conflict.
-
-* The constructor for a write-stream into the odb now takes
- `git_off_t` instead of `size_t` for the size of the blob, which
- allows putting large files into the odb on 32-bit systems.
-
-* The remote's push and pull URLs now honor the url.$URL.insteadOf
- configuration. This allows modifying URL prefixes to a custom
- value via gitconfig.
-
-* `git_diff_foreach`, `git_diff_blobs`, `git_diff_blob_to_buffer`,
- and `git_diff_buffers` now accept a new binary callback of type
- `git_diff_binary_cb` that includes the binary diff information.
-
-* The race condition mitigations described in `racy-git.txt` have been
- implemented.
-
-* If libcurl is installed, we will use it to connect to HTTP(S)
- servers.
-
-### API additions
-
-* The `git_merge_options` gained a `file_flags` member.
-
-* Parsing and retrieving a configuration value as a path is exposed
- via `git_config_parse_path()` and `git_config_get_path()`
- respectively.
-
-* `git_repository_set_ident()` and `git_repository_ident()` serve to
- set and query which identity will be used when writing to the
- reflog.
-
-* `git_config_entry_free()` frees a config entry.
-
-* `git_config_get_string_buf()` provides a way to safely retrieve a
- string from a non-snapshot configuration.
-
-* `git_annotated_commit_from_revspec()` allows to get an annotated
- commit from an extended sha synatx string.
-
-* `git_repository_set_head_detached_from_annotated()`,
- `git_branch_create_from_annotated()` and
- `git_reset_from_annotated()` allow for the caller to provide an
- annotated commit through which they can control what expression is
- put into the reflog as the source/target.
-
-* `git_index_add_frombuffer()` can now create a blob from memory
- buffer and add it to the index which is attached to a repository.
-
-* The structure `git_fetch_options` has been added to determine the
- runtime configuration for fetching, such as callbacks, pruning and
- autotag behaviour. It has the runtime initializer
- `git_fetch_init_options()`.
-
-* The enum `git_fetch_prune_t` has been added, letting you specify the
- pruning behaviour for a fetch.
-
-* A push operation will notify the caller of what updates it indends
- to perform on the remote, which provides similar information to
- git's pre-push hook.
-
-* `git_stash_apply()` can now apply a stashed state from the stash list,
- placing the data into the working directory and index.
-
-* `git_stash_pop()` will apply a stashed state (like `git_stash_apply()`)
- but will remove the stashed state after a successful application.
-
-* A new error code `GIT_EEOF` indicates an early EOF from the
- server. This typically indicates an error with the URL or
- configuration of the server, and tools can use this to show messages
- about failing to communicate with the server.
-
-* A new error code `GIT_EINVALID` indicates that an argument to a
- function is invalid, or an invalid operation was requested.
-
-* `git_diff_index_to_workdir()` and `git_diff_tree_to_index()` will now
- produce deltas of type `GIT_DELTA_CONFLICTED` to indicate that the index
- side of the delta is a conflict.
-
-* The `git_status` family of functions will now produce status of type
- `GIT_STATUS_CONFLICTED` to indicate that a conflict exists for that file
- in the index.
-
-* `git_index_entry_is_conflict()` is a utility function to determine if
- a given index entry has a non-zero stage entry, indicating that it is
- one side of a conflict.
-
-* It is now possible to pass a keypair via a buffer instead of a
- path. For this, `GIT_CREDTYPE_SSH_MEMORY` and
- `git_cred_ssh_key_memory_new()` have been added.
-
-* `git_filter_list_contains` will indicate whether a particular
- filter will be run in the given filter list.
-
-* `git_commit_header_field()` has been added, which allows retrieving
- the contents of an arbitrary header field.
-
-* `git_submodule_set_branch()` allows to set the configured branch for
- a submodule.
-
-### API removals
-
-* `git_remote_save()` and `git_remote_clear_refspecs()` have been
- removed. Remote's configuration is changed via the configuration
- directly or through a convenience function which performs changes to
- the configuration directly.
-
-* `git_remote_set_callbacks()`, `git_remote_get_callbacks()` and
- `git_remote_set_transport()` have been removed and the remote no
- longer stores this configuration.
-
-* `git_remote_set_fetch_refpecs()` and
- `git_remote_set_push_refspecs()` have been removed. There is no
- longer a way to set the base refspecs at run-time.
-
-* `git_submodule_save()` has been removed. The submodules are no
- longer configured via the objects.
-
-* `git_submodule_reload_all()` has been removed as we no longer cache
- submodules.
-
-### Breaking API changes
-
-* `git_smart_subtransport_cb` now has a `param` parameter.
-
-* The `git_merge_options` structure member `flags` has been renamed
- to `tree_flags`.
-
-* The `git_merge_file_options` structure member `flags` is now
- an unsigned int. It was previously a `git_merge_file_flags_t`.
-
-* `GIT_CHECKOUT_SAFE_CREATE` has been removed. Most users will generally
- be able to switch to `GIT_CHECKOUT_SAFE`, but if you require missing
- file handling during checkout, you may now use `GIT_CHECKOUT_SAFE |
- GIT_CHECKOUT_RECREATE_MISSING`.
-
-* The `git_clone_options` and `git_submodule_update_options`
- structures no longer have a `signature` field.
-
-* The following functions have removed the signature and/or log message
- parameters in favour of git-emulating ones.
-
- * `git_branch_create()`, `git_branch_move()`
- * `git_rebase_init()`, `git_rebase_abort()`
- * `git_reference_symbolic_create_matching()`,
- `git_reference_symbolic_create()`, `git_reference_create()`,
- `git_reference_create_matching()`,
- `git_reference_symbolic_set_target()`,
- `git_reference_set_target()`, `git_reference_rename()`
- * `git_remote_update_tips()`, `git_remote_fetch()`, `git_remote_push()`
- * `git_repository_set_head()`,
- `git_repository_set_head_detached()`,
- `git_repository_detach_head()`
- * `git_reset()`
-
-* `git_config_get_entry()` now gives back a ref-counted
- `git_config_entry`. You must free it when you no longer need it.
-
-* `git_config_get_string()` will return an error if used on a
- non-snapshot configuration, as there can be no guarantee that the
- returned pointer is valid.
-
-* `git_note_default_ref()` now uses a `git_buf` to return the string,
- as the string is otherwise not guaranteed to stay allocated.
-
-* `git_rebase_operation_current()` will return `GIT_REBASE_NO_OPERATION`
- if it is called immediately after creating a rebase session but before
- you have applied the first patch.
-
-* `git_rebase_options` now contains a `git_checkout_options` struct
- that will be used for functions that modify the working directory,
- namely `git_rebase_init`, `git_rebase_next` and
- `git_rebase_abort`. As a result, `git_rebase_open` now also takes
- a `git_rebase_options` and only the `git_rebase_init` and
- `git_rebase_open` functions take a `git_rebase_options`, where they
- will persist the options to subsequent `git_rebase` calls.
-
-* The `git_clone_options` struct now has fetch options in a
- `fetch_opts` field instead of remote callbacks in
- `remote_callbacks`.
-
-* The remote callbacks has gained a new member `push_negotiation`
- which gets called before sending the update commands to the server.
-
-* The following functions no longer act on a remote instance but
- change the repository's configuration. Their signatures have changed
- accordingly:
-
- * `git_remote_set_url()`, `git_remote_seturl()`
- * `git_remote_add_fetch()`, `git_remote_add_push()` and
- * `git_remote_set_autotag()`
-
-* `git_remote_connect()` and `git_remote_prune()` now take a pointer
- to the callbacks.
-
-* `git_remote_fetch()` and `git_remote_download()` now take a pointer
- to fetch options which determine the runtime configuration.
-
-* The `git_remote_autotag_option_t` values have been changed. It has
- gained a `_UNSPECIFIED` default value to specify no override for the
- configured setting.
-
-* `git_remote_update_tips()` now takes a pointer to the callbacks as
- well as a boolean whether to write `FETCH_HEAD` and the autotag
- setting.
-
-* `git_remote_create_anonymous()` no longer takes a fetch refspec as
- url-only remotes cannot have configured refspecs.
-
-* The `git_submodule_update_options` struct now has fetch options in
- the `fetch_opts` field instead of callbacks in the
- `remote_callbacks` field.
-
-* The following functions no longer act on a submodule instance but
- change the repository's configuration. Their signatures have changed
- accordingly:
-
- * `git_submodule_set_url()`, `git_submodule_set_ignore()`,
- `git_submodule_set_update()`,
- `git_submodule_set_fetch_recurse_submodules()`.
-
-* `git_submodule_status()` no longer takes a submodule instance but a
- repsitory, a submodule name and an ignore setting.
-
-* The `push` function in the `git_transport` interface now takes a
- pointer to the remote callbacks.
-
-* The `git_index_entry` struct's fields' types have been changed to
- more accurately reflect what is in fact stored in the
- index. Specifically, time and file size are 32 bits intead of 64, as
- these values are truncated.
-
-* `GIT_EMERGECONFLICT` is now `GIT_ECONFLICT`, which more accurately
- describes the nature of the error.
-
-* It is no longer allowed to call `git_buf_grow()` on buffers
- borrowing the memory they point to.
-
-v0.22
-------
-
-### Changes or improvements
-
-* `git_signature_new()` now requires a non-empty email address.
-
-* Use CommonCrypto libraries for SHA-1 calculation on Mac OS X.
-
-* Disable SSL compression and SSLv2 and SSLv3 ciphers in favor of TLSv1
- in OpenSSL.
-
-* The fetch behavior of remotes with autotag set to `GIT_REMOTE_DOWNLOAD_TAGS_ALL`
- has been changed to match git 1.9.0 and later. In this mode, libgit2 now
- fetches all tags in addition to whatever else needs to be fetched.
-
-* `git_checkout()` now handles case-changing renames correctly on
- case-insensitive filesystems; for example renaming "readme" to "README".
-
-* The search for libssh2 is now done via pkg-config instead of a
- custom search of a few directories.
-
-* Add support for core.protectHFS and core.protectNTFS. Add more
- validation for filenames which we write such as references.
-
-* The local transport now generates textual progress output like
- git-upload-pack does ("counting objects").
-
-* `git_checkout_index()` can now check out an in-memory index that is not
- necessarily the repository's index, so you may check out an index
- that was produced by git_merge and friends while retaining the cached
- information.
-
-* Remove the default timeout for receiving / sending data over HTTP using
- the WinHTTP transport layer.
-
-* Add SPNEGO (Kerberos) authentication using GSSAPI on Unix systems.
-
-* Provide built-in objects for the empty blob (e69de29) and empty
- tree (4b825dc) objects.
-
-* The index' tree cache is now filled upon read-tree and write-tree
- and the cache is written to disk.
-
-* LF -> CRLF filter refuses to handle mixed-EOL files
-
-* LF -> CRLF filter now runs when * text = auto (with Git for Windows 1.9.4)
-
-* File unlocks are atomic again via rename. Read-only files on Windows are
- made read-write if necessary.
-
-* Share open packfiles across repositories to share descriptors and mmaps.
-
-* Use a map for the treebuilder, making insertion O(1)
-
-* The build system now accepts an option EMBED_SSH_PATH which when set
- tells it to include a copy of libssh2 at the given location. This is
- enabled for MSVC.
-
-* Add support for refspecs with the asterisk in the middle of a
- pattern.
-
-* Fetching now performs opportunistic updates. To achieve this, we
- introduce a difference between active and passive refspecs, which
- make `git_remote_download()` and `git_remote_fetch()` to take a list of
- resfpecs to be the active list, similarly to how git fetch accepts a
- list on the command-line.
-
-* The THREADSAFE option to build libgit2 with threading support has
- been flipped to be on by default.
-
-* The remote object has learnt to prune remote-tracking branches. If
- the remote is configured to do so, this will happen via
- `git_remote_fetch()`. You can also call `git_remote_prune()` after
- connecting or fetching to perform the prune.
-
-
-### API additions
-
-* Introduce `git_buf_text_is_binary()` and `git_buf_text_contains_nul()` for
- consumers to perform binary detection on a git_buf.
-
-* `git_branch_upstream_remote()` has been introduced to provide the
- branch.<name>.remote configuration value.
-
-* Introduce `git_describe_commit()` and `git_describe_workdir()` to provide
- a description of the current commit (and working tree, respectively)
- based on the nearest tag or reference
-
-* Introduce `git_merge_bases()` and the `git_oidarray` type to expose all
- merge bases between two commits.
-
-* Introduce `git_merge_bases_many()` to expose all merge bases between
- multiple commits.
-
-* Introduce rebase functionality (using the merge algorithm only).
- Introduce `git_rebase_init()` to begin a new rebase session,
- `git_rebase_open()` to open an in-progress rebase session,
- `git_rebase_commit()` to commit the current rebase operation,
- `git_rebase_next()` to apply the next rebase operation,
- `git_rebase_abort()` to abort an in-progress rebase and `git_rebase_finish()`
- to complete a rebase operation.
-
-* Introduce `git_note_author()` and `git_note_committer()` to get the author
- and committer information on a `git_note`, respectively.
-
-* A factory function for ssh has been added which allows to change the
- path of the programs to execute for receive-pack and upload-pack on
- the server, `git_transport_ssh_with_paths()`.
-
-* The ssh transport supports asking the remote host for accepted
- credential types as well as multiple challeges using a single
- connection. This requires to know which username you want to connect
- as, so this introduces the USERNAME credential type which the ssh
- transport will use to ask for the username.
-
-* The `GIT_EPEEL` error code has been introduced when we cannot peel a tag
- to the requested object type; if the given object otherwise cannot be
- peeled, `GIT_EINVALIDSPEC` is returned.
-
-* Introduce `GIT_REPOSITORY_INIT_RELATIVE_GITLINK` to use relative paths
- when writing gitlinks, as is used by git core for submodules.
-
-* `git_remote_prune()` has been added. See above for description.
-
-
-* Introduce reference transactions, which allow multiple references to
- be locked at the same time and updates be queued. This also allows
- us to safely update a reflog with arbitrary contents, as we need to
- do for stash.
-
-### API removals
-
-* `git_remote_supported_url()` and `git_remote_is_valid_url()` have been
- removed as they have become essentially useless with rsync-style ssh paths.
-
-* `git_clone_into()` and `git_clone_local_into()` have been removed from the
- public API in favour of `git_clone callbacks`.
-
-* The option to ignore certificate errors via `git_remote_cert_check()`
- is no longer present. Instead, `git_remote_callbacks` has gained a new
- entry which lets the user perform their own certificate checks.
-
-### Breaking API changes
-
-* `git_cherry_pick()` is now `git_cherrypick()`.
-
-* The `git_submodule_update()` function was renamed to
- `git_submodule_update_strategy()`. `git_submodule_update()` is now used to
- provide functionalty similar to "git submodule update".
-
-* `git_treebuilder_create()` was renamed to `git_treebuilder_new()` to better
- reflect it being a constructor rather than something which writes to
- disk.
-
-* `git_treebuilder_new()` (was `git_treebuilder_create()`) now takes a
- repository so that it can query repository configuration.
- Subsequently, `git_treebuilder_write()` no longer takes a repository.
-
-* `git_threads_init()` and `git_threads_shutdown()` have been renamed to
- `git_libgit2_init()` and `git_libgit2_shutdown()` to better explain what
- their purpose is, as it's grown to be more than just about threads.
-
-* `git_libgit2_init()` and `git_libgit2_shutdown()` now return the number of
- initializations of the library, so consumers may schedule work on the
- first initialization.
-
-* The `git_transport_register()` function no longer takes a priority and takes
- a URL scheme name (eg "http") instead of a prefix like "http://"
-
-* `git_index_name_entrycount()` and `git_index_reuc_entrycount()` now
- return size_t instead of unsigned int.
-
-* The `context_lines` and `interhunk_lines` fields in `git_diff`_options are
- now `uint32_t` instead of `uint16_t`. This allows to set them to `UINT_MAX`,
- in effect asking for "infinite" context e.g. to iterate over all the
- unmodified lines of a diff.
-
-* `git_status_file()` now takes an exact path. Use `git_status_list_new()` if
- pathspec searching is needed.
-
-* `git_note_create()` has changed the position of the notes reference
- name to match `git_note_remove()`.
-
-* Rename `git_remote_load()` to `git_remote_lookup()` to bring it in line
- with the rest of the lookup functions.
-
-* `git_remote_rename()` now takes the repository and the remote's
- current name. Accepting a remote indicates we want to change it,
- which we only did partially. It is much clearer if we accept a name
- and no loaded objects are changed.
-
-* `git_remote_delete()` now accepts the repository and the remote's name
- instead of a loaded remote.
-
-* `git_merge_head` is now `git_annotated_commit`, to better reflect its usage
- for multiple functions (including rebase)
-
-* The `git_clone_options` struct no longer provides the `ignore_cert_errors` or
- `remote_name` members for remote customization.
-
- Instead, the `git_clone_options` struct has two new members, `remote_cb` and
- `remote_cb_payload`, which allow the caller to completely override the remote
- creation process. If needed, the caller can use this callback to give their
- remote a name other than the default (origin) or disable cert checking.
-
- The `remote_callbacks` member has been preserved for convenience, although it
- is not used when a remote creation callback is supplied.
-
-* The `git_clone`_options struct now provides `repository_cb` and
- `repository_cb_payload` to allow the user to create a repository with
- custom options.
-
-* The `git_push` struct to perform a push has been replaced with
- `git_remote_upload()`. The refspecs and options are passed as a
- function argument. `git_push_update_tips()` is now also
- `git_remote_update_tips()` and the callbacks are in the same struct as
- the rest.
-
-* The `git_remote_set_transport()` function now sets a transport factory function,
- rather than a pre-existing transport instance.
-
-* The `git_transport` structure definition has moved into the sys/transport.h
- file.
-
-* libgit2 no longer automatically sets the OpenSSL locking
- functions. This is not something which we can know to do. A
- last-resort convenience function is provided in sys/openssl.h,
- `git_openssl_set_locking()` which can be used to set the locking.
+++ /dev/null
-# CMake build script for the libgit2 project
-#
-# Building (out of source build):
-# > mkdir build && cd build
-# > cmake .. [-DSETTINGS=VALUE]
-# > cmake --build .
-#
-# Testing:
-# > ctest -V
-#
-# Install:
-# > cmake --build . --target install
-
-PROJECT(libgit2 C)
-CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
-CMAKE_POLICY(SET CMP0015 NEW)
-
-# Add find modules to the path
-SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
-
-INCLUDE(CheckLibraryExists)
-INCLUDE(CheckFunctionExists)
-INCLUDE(CheckSymbolExists)
-INCLUDE(CheckStructHasMember)
-INCLUDE(AddCFlagIfSupported)
-INCLUDE(FindPkgConfig)
-
-# Build options
-#
-OPTION( SONAME "Set the (SO)VERSION of the target" ON )
-OPTION( BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON )
-OPTION( THREADSAFE "Build libgit2 as threadsafe" ON )
-OPTION( BUILD_CLAR "Build Tests using the Clar suite" ON )
-OPTION( BUILD_EXAMPLES "Build library usage example apps" OFF )
-OPTION( TAGS "Generate tags" OFF )
-OPTION( PROFILE "Generate profiling information" OFF )
-OPTION( ENABLE_TRACE "Enables tracing support" OFF )
-OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
-
-OPTION( USE_ICONV "Link with and use iconv library" OFF )
-OPTION( USE_SSH "Link with libssh to enable SSH support" ON )
-OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF )
-OPTION( VALGRIND "Configure build for valgrind" OFF )
-OPTION( CURL "Use curl for HTTP if available" ON)
-OPTION( DEBUG_POOL "Enable debug pool allocator" OFF )
-
-IF(DEBUG_POOL)
- ADD_DEFINITIONS(-DGIT_DEBUG_POOL)
-ENDIF()
-
-IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
- SET( USE_ICONV ON )
- FIND_PACKAGE(Security)
- FIND_PACKAGE(CoreFoundation REQUIRED)
-ENDIF()
-
-IF(MSVC)
- # This option is only available when building with MSVC. By default, libgit2
- # is build using the cdecl calling convention, which is useful if you're
- # writing C. However, the CLR and Win32 API both expect stdcall.
- #
- # If you are writing a CLR program and want to link to libgit2, you'll want
- # to turn this on by invoking CMake with the "-DSTDCALL=ON" argument.
- OPTION( STDCALL "Build libgit2 with the __stdcall convention" OFF )
-
- # This option must match the settings used in your program, in particular if you
- # are linking statically
- OPTION( STATIC_CRT "Link the static CRT libraries" ON )
-
- # If you want to embed a copy of libssh2 into libgit2, pass a
- # path to libssh2
- OPTION( EMBED_SSH_PATH "Path to libssh2 to embed (Windows)" OFF )
-
- ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS)
- ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
- ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE)
-ENDIF()
-
-
-IF(WIN32)
- # By default, libgit2 is built with WinHTTP. To use the built-in
- # HTTP transport, invoke CMake with the "-DWINHTTP=OFF" argument.
- OPTION( WINHTTP "Use Win32 WinHTTP routines" ON )
-ENDIF()
-
-IF(MSVC)
- # Enable MSVC CRTDBG memory leak reporting when in debug mode.
- OPTION(MSVC_CRTDBG "Enable CRTDBG memory leak reporting" OFF)
-ENDIF()
-
-IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
- OPTION( USE_OPENSSL "Link with and use openssl library" ON )
-ENDIF()
-
-CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtim "sys/types.h;sys/stat.h"
- HAVE_STRUCT_STAT_ST_MTIM LANGUAGE C)
-CHECK_STRUCT_HAS_MEMBER ("struct stat" st_mtimespec "sys/types.h;sys/stat.h"
- HAVE_STRUCT_STAT_ST_MTIMESPEC LANGUAGE C)
-CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtime_nsec sys/stat.h
- HAVE_STRUCT_STAT_MTIME_NSEC LANGUAGE C)
-
-IF (HAVE_STRUCT_STAT_ST_MTIM)
- CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtim.tv_nsec sys/stat.h
- HAVE_STRUCT_STAT_NSEC LANGUAGE C)
-ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC)
- CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtimespec.tv_nsec sys/stat.h
- HAVE_STRUCT_STAT_NSEC LANGUAGE C)
-ELSE ()
- SET( HAVE_STRUCT_STAT_NSEC ON )
-ENDIF()
-
-IF (HAVE_STRUCT_STAT_NSEC OR WIN32)
- OPTION( USE_NSEC "Care about sub-second file mtimes and ctimes" ON )
-ENDIF()
-
-# This variable will contain the libraries we need to put into
-# libgit2.pc's Requires.private. That is, what we're linking to or
-# what someone who's statically linking us needs to link to.
-SET(LIBGIT2_PC_REQUIRES "")
-# This will be set later if we use the system's http-parser library or
-# use iconv (OSX) and will be written to the Libs.private field in the
-# pc file.
-SET(LIBGIT2_PC_LIBS "")
-
-# Installation paths
-#
-SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.")
-SET(LIB_INSTALL_DIR lib CACHE PATH "Where to install libraries to.")
-SET(INCLUDE_INSTALL_DIR include CACHE PATH "Where to install headers to.")
-
-# Set a couple variables to be substituted inside the .pc file.
-# We can't just use LIB_INSTALL_DIR in the .pc file, as passing them as absolue
-# or relative paths is both valid and supported by cmake.
-SET (PKGCONFIG_PREFIX ${CMAKE_INSTALL_PREFIX})
-
-IF(IS_ABSOLUTE ${LIB_INSTALL_DIR})
- SET (PKGCONFIG_LIBDIR ${LIB_INSTALL_DIR})
-ELSE(IS_ABSOLUTE ${LIB_INSTALL_DIR})
- SET (PKGCONFIG_LIBDIR "\${prefix}/${LIB_INSTALL_DIR}")
-ENDIF (IS_ABSOLUTE ${LIB_INSTALL_DIR})
-
-IF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
- SET (PKGCONFIG_INCLUDEDIR ${INCLUDE_INSTALL_DIR})
-ELSE(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
- SET (PKGCONFIG_INCLUDEDIR "\${prefix}/${INCLUDE_INSTALL_DIR}")
-ENDIF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
-
-FUNCTION(TARGET_OS_LIBRARIES target)
- IF(WIN32)
- TARGET_LINK_LIBRARIES(${target} ws2_32)
- ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
- TARGET_LINK_LIBRARIES(${target} socket nsl)
- LIST(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
- SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
- ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Haiku")
- TARGET_LINK_LIBRARIES(${target} network)
- LIST(APPEND LIBGIT2_PC_LIBS "-lnetwork")
- SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
- ENDIF()
- CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" NEED_LIBRT)
- IF(NEED_LIBRT)
- TARGET_LINK_LIBRARIES(${target} rt)
- LIST(APPEND LIBGIT2_PC_LIBS "-lrt")
- SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
- ENDIF()
-
- IF(THREADSAFE)
- TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
- LIST(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
- SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
- ENDIF()
-ENDFUNCTION()
-
-# This function splits the sources files up into their appropriate
-# subdirectories. This is especially useful for IDEs like Xcode and
-# Visual Studio, so that you can navigate into the libgit2_clar project,
-# and see the folders within the tests folder (instead of just seeing all
-# source and tests in a single folder.)
-FUNCTION(IDE_SPLIT_SOURCES target)
- IF(MSVC_IDE OR CMAKE_GENERATOR STREQUAL Xcode)
- GET_TARGET_PROPERTY(sources ${target} SOURCES)
- FOREACH(source ${sources})
- IF(source MATCHES ".*/")
- STRING(REPLACE ${CMAKE_CURRENT_SOURCE_DIR}/ "" rel ${source})
- IF(rel)
- STRING(REGEX REPLACE "/([^/]*)$" "" rel ${rel})
- IF(rel)
- STRING(REPLACE "/" "\\\\" rel ${rel})
- SOURCE_GROUP(${rel} FILES ${source})
- ENDIF()
- ENDIF()
- ENDIF()
- ENDFOREACH()
- ENDIF()
-ENDFUNCTION()
-
-FILE(STRINGS "include/git2/version.h" GIT2_HEADER REGEX "^#define LIBGIT2_VERSION \"[^\"]*\"$")
-
-STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"([0-9]+).*$" "\\1" LIBGIT2_VERSION_MAJOR "${GIT2_HEADER}")
-STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_MINOR "${GIT2_HEADER}")
-STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_REV "${GIT2_HEADER}")
-SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${LIBGIT2_VERSION_REV}")
-
-FILE(STRINGS "include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION [0-9]+$")
-STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "${GIT2_HEADER_SOVERSION}")
-
-# Find required dependencies
-INCLUDE_DIRECTORIES(src include)
-
-IF (SECURITY_FOUND)
- # OS X 10.7 and older do not have some functions we use, fall back to OpenSSL there
- CHECK_LIBRARY_EXISTS("${SECURITY_DIRS}" SSLCreateContext "Security/SecureTransport.h" HAVE_NEWER_SECURITY)
- IF (HAVE_NEWER_SECURITY)
- MESSAGE("-- Found Security ${SECURITY_DIRS}")
- LIST(APPEND LIBGIT2_PC_LIBS "-framework Security")
- ELSE()
- MESSAGE("-- Security framework is too old, falling back to OpenSSL")
- SET(SECURITY_FOUND "NO")
- SET(SECURITY_DIRS "")
- SET(SECURITY_DIR "")
- SET(USE_OPENSSL "ON")
- ENDIF()
-ENDIF()
-
-IF (COREFOUNDATION_FOUND)
- MESSAGE("-- Found CoreFoundation ${COREFOUNDATION_DIRS}")
- LIST(APPEND LIBGIT2_PC_LIBS "-framework CoreFoundation")
-ENDIF()
-
-
-IF (WIN32 AND EMBED_SSH_PATH)
- FILE(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
- INCLUDE_DIRECTORIES("${EMBED_SSH_PATH}/include")
- FILE(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"")
- ADD_DEFINITIONS(-DGIT_SSH)
-ENDIF()
-
-IF (WIN32 AND WINHTTP)
- ADD_DEFINITIONS(-DGIT_WINHTTP)
- INCLUDE_DIRECTORIES(deps/http-parser)
- FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
-
- # Since MinGW does not come with headers or an import library for winhttp,
- # we have to include a private header and generate our own import library
- IF (MINGW)
- FIND_PROGRAM(DLLTOOL dlltool CMAKE_FIND_ROOT_PATH_BOTH)
- IF (NOT DLLTOOL)
- MESSAGE(FATAL_ERROR "Could not find dlltool command")
- ENDIF ()
-
- SET(LIBWINHTTP_PATH "${CMAKE_CURRENT_BINARY_DIR}/deps/winhttp")
- FILE(MAKE_DIRECTORY ${LIBWINHTTP_PATH})
-
- IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
- set(WINHTTP_DEF "${CMAKE_CURRENT_SOURCE_DIR}/deps/winhttp/winhttp64.def")
- ELSE()
- set(WINHTTP_DEF "${CMAKE_CURRENT_SOURCE_DIR}/deps/winhttp/winhttp.def")
- ENDIF()
-
- ADD_CUSTOM_COMMAND(
- OUTPUT ${LIBWINHTTP_PATH}/libwinhttp.a
- COMMAND ${DLLTOOL} -d ${WINHTTP_DEF} -k -D winhttp.dll -l libwinhttp.a
- DEPENDS ${WINHTTP_DEF}
- WORKING_DIRECTORY ${LIBWINHTTP_PATH}
- )
-
- SET_SOURCE_FILES_PROPERTIES(
- ${CMAKE_CURRENT_SOURCE_DIR}/src/transports/winhttp.c
- PROPERTIES OBJECT_DEPENDS ${LIBWINHTTP_PATH}/libwinhttp.a
- )
-
- INCLUDE_DIRECTORIES(deps/winhttp)
- LINK_DIRECTORIES(${LIBWINHTTP_PATH})
- ENDIF ()
-
- LINK_LIBRARIES(winhttp rpcrt4 crypt32 ole32)
- LIST(APPEND LIBGIT2_PC_LIBS "-lwinhttp" "-lrpcrt4" "-lcrypt32" "-lole32")
-ELSE ()
- IF (CURL)
- PKG_CHECK_MODULES(CURL libcurl)
- ENDIF ()
-
- IF (NOT AMIGA AND USE_OPENSSL)
- FIND_PACKAGE(OpenSSL)
- ENDIF ()
-
- IF (CURL_FOUND)
- ADD_DEFINITIONS(-DGIT_CURL)
- INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIRS})
- LINK_DIRECTORIES(${CURL_LIBRARY_DIRS})
- LINK_LIBRARIES(${CURL_LIBRARIES})
- LIST(APPEND LIBGIT2_PC_LIBS ${CURL_LDFLAGS})
- ENDIF()
-
- FIND_PACKAGE(HTTP_Parser)
- IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
- INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
- LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
- LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser")
- ELSE()
- MESSAGE(STATUS "http-parser was not found or is too old; using bundled 3rd-party sources.")
- INCLUDE_DIRECTORIES(deps/http-parser)
- FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
- ENDIF()
-ENDIF()
-
-# Specify sha1 implementation
-IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin")
- ADD_DEFINITIONS(-DWIN32_SHA1)
- FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
-ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
- ADD_DEFINITIONS(-DGIT_COMMON_CRYPTO)
-ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
- ADD_DEFINITIONS(-DOPENSSL_SHA1)
- IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
- LIST(APPEND LIBGIT2_PC_LIBS "-lssl")
- ELSE()
- SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
- ENDIF ()
-ELSE()
- FILE(GLOB SRC_SHA1 src/hash/hash_generic.c)
-ENDIF()
-
-# Enable tracing
-IF (ENABLE_TRACE STREQUAL "ON")
- ADD_DEFINITIONS(-DGIT_TRACE)
-ENDIF()
-
-# Include POSIX regex when it is required
-IF(WIN32 OR AMIGA OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
- INCLUDE_DIRECTORIES(deps/regex)
- SET(SRC_REGEX deps/regex/regex.c)
-ENDIF()
-
-# Optional external dependency: zlib
-FIND_PACKAGE(ZLIB)
-IF (ZLIB_FOUND)
- INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
- LINK_LIBRARIES(${ZLIB_LIBRARIES})
- IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
- LIST(APPEND LIBGIT2_PC_LIBS "-lz")
- ELSE()
- SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} zlib")
- ENDIF()
-ELSE()
- MESSAGE(STATUS "zlib was not found; using bundled 3rd-party sources." )
- INCLUDE_DIRECTORIES(deps/zlib)
- ADD_DEFINITIONS(-DNO_VIZ -DSTDC -DNO_GZIP)
- FILE(GLOB SRC_ZLIB deps/zlib/*.c deps/zlib/*.h)
-ENDIF()
-
-# Optional external dependency: libssh2
-IF (USE_SSH)
- PKG_CHECK_MODULES(LIBSSH2 libssh2)
-ENDIF()
-IF (LIBSSH2_FOUND)
- ADD_DEFINITIONS(-DGIT_SSH)
- INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIRS})
- LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS})
- LIST(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS})
- #SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${LIBSSH2_LDFLAGS}")
- SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
-
- CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
- IF (HAVE_LIBSSH2_MEMORY_CREDENTIALS)
- ADD_DEFINITIONS(-DGIT_SSH_MEMORY_CREDENTIALS)
- ENDIF()
-ELSE()
- MESSAGE(STATUS "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.")
-ENDIF()
-
-# Optional external dependency: libgssapi
-IF (USE_GSSAPI)
- FIND_PACKAGE(GSSAPI)
-ENDIF()
-IF (GSSAPI_FOUND)
- ADD_DEFINITIONS(-DGIT_GSSAPI)
-ENDIF()
-
-# Optional external dependency: iconv
-IF (USE_ICONV)
- FIND_PACKAGE(Iconv)
-ENDIF()
-IF (ICONV_FOUND)
- ADD_DEFINITIONS(-DGIT_USE_ICONV)
- INCLUDE_DIRECTORIES(${ICONV_INCLUDE_DIR})
- LIST(APPEND LIBGIT2_PC_LIBS ${ICONV_LIBRARIES})
-ENDIF()
-
-# Platform specific compilation flags
-IF (MSVC)
-
- STRING(REPLACE "/Zm1000" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
-
- # /GF - String pooling
- # /MP - Parallel build
- SET(CMAKE_C_FLAGS "/GF /MP /nologo ${CMAKE_C_FLAGS}")
-
- IF (STDCALL)
- # /Gz - stdcall calling convention
- SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Gz")
- ENDIF ()
-
- IF (STATIC_CRT)
- SET(CRT_FLAG_DEBUG "/MTd")
- SET(CRT_FLAG_RELEASE "/MT")
- ELSE()
- SET(CRT_FLAG_DEBUG "/MDd")
- SET(CRT_FLAG_RELEASE "/MD")
- ENDIF()
-
- IF (MSVC_CRTDBG)
- SET(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG} /DGIT_MSVC_CRTDBG")
- SET(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}" "Dbghelp.lib")
- ENDIF()
-
- # /Zi - Create debugging information
- # /Od - Disable optimization
- # /D_DEBUG - #define _DEBUG
- # /MTd - Statically link the multithreaded debug version of the CRT
- # /MDd - Dynamically link the multithreaded debug version of the CRT
- # /RTC1 - Run time checks
- SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Zi /Od /D_DEBUG /RTC1 ${CRT_FLAG_DEBUG}")
-
- # /DNDEBUG - Disables asserts
- # /MT - Statically link the multithreaded release version of the CRT
- # /MD - Dynamically link the multithreaded release version of the CRT
- # /O2 - Optimize for speed
- # /Oy - Enable frame pointer omission (FPO) (otherwise CMake will automatically turn it off)
- # /GL - Link time code generation (whole program optimization)
- # /Gy - Function-level linking
- SET(CMAKE_C_FLAGS_RELEASE "/DNDEBUG /O2 /Oy /GL /Gy ${CRT_FLAG_RELEASE}")
-
- # /Oy- - Disable frame pointer omission (FPO)
- SET(CMAKE_C_FLAGS_RELWITHDEBINFO "/DNDEBUG /Zi /O2 /Oy- /GL /Gy ${CRT_FLAG_RELEASE}")
-
- # /O1 - Optimize for size
- SET(CMAKE_C_FLAGS_MINSIZEREL "/DNDEBUG /O1 /Oy /GL /Gy ${CRT_FLAG_RELEASE}")
-
- # /DYNAMICBASE - Address space load randomization (ASLR)
- # /NXCOMPAT - Data execution prevention (DEP)
- # /LARGEADDRESSAWARE - >2GB user address space on x86
- # /VERSION - Embed version information in PE header
- SET(CMAKE_EXE_LINKER_FLAGS "/DYNAMICBASE /NXCOMPAT /LARGEADDRESSAWARE /VERSION:${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}")
-
- # /DEBUG - Create a PDB
- # /LTCG - Link time code generation (whole program optimization)
- # /OPT:REF /OPT:ICF - Fold out duplicate code at link step
- # /INCREMENTAL:NO - Required to use /LTCG
- # /DEBUGTYPE:cv,fixup - Additional data embedded in the PDB (requires /INCREMENTAL:NO, so not on for Debug)
- SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG")
- SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "/RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
- SET(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO /DEBUGTYPE:cv,fixup")
- SET(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "/RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
-
- # Same linker settings for DLL as EXE
- SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
- SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
- SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
- SET(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
- SET(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL}")
-
- SET(WIN_RC "src/win32/git2.rc")
-
- # Precompiled headers
-
-ELSE ()
- SET(CMAKE_C_FLAGS "-D_GNU_SOURCE -Wall -Wextra ${CMAKE_C_FLAGS}")
-
- IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
- SET(CMAKE_C_FLAGS "-std=c99 -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS ${CMAKE_C_FLAGS}")
- ENDIF()
-
- IF (WIN32 AND NOT CYGWIN)
- SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
- ENDIF ()
-
- IF (MINGW OR MSYS) # MinGW and MSYS always do PIC and complain if we tell them to
- STRING(REGEX REPLACE "-fPIC" "" CMAKE_SHARED_LIBRARY_C_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
- ELSEIF (BUILD_SHARED_LIBS)
- ADD_C_FLAG_IF_SUPPORTED(-fvisibility=hidden)
-
- SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
- ENDIF ()
-
- IF (MINGW)
- # MinGW >= 3.14 uses the C99-style stdio functions
- # automatically, but forks like mingw-w64 still want
- # us to define this in order to use them
- ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1)
- ENDIF ()
-
- ADD_C_FLAG_IF_SUPPORTED(-Wdocumentation)
- ADD_C_FLAG_IF_SUPPORTED(-Wno-missing-field-initializers)
- ADD_C_FLAG_IF_SUPPORTED(-Wstrict-aliasing=2)
- ADD_C_FLAG_IF_SUPPORTED(-Wstrict-prototypes)
- ADD_C_FLAG_IF_SUPPORTED(-Wdeclaration-after-statement)
- ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-const-variable)
- ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-function)
-
- IF (APPLE) # Apple deprecated OpenSSL
- ADD_C_FLAG_IF_SUPPORTED(-Wno-deprecated-declarations)
- ENDIF()
-
- IF (PROFILE)
- SET(CMAKE_C_FLAGS "-pg ${CMAKE_C_FLAGS}")
- SET(CMAKE_EXE_LINKER_FLAGS "-pg ${CMAKE_EXE_LINKER_FLAGS}")
- ENDIF ()
-ENDIF()
-
-CHECK_SYMBOL_EXISTS(regcomp_l "regex.h;xlocale.h" HAVE_REGCOMP_L)
-IF (HAVE_REGCOMP_L)
- ADD_DEFINITIONS(-DHAVE_REGCOMP_L)
-ENDIF ()
-
-CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
-IF (HAVE_FUTIMENS)
- ADD_DEFINITIONS(-DHAVE_FUTIMENS)
-ENDIF ()
-
-CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R)
-IF (HAVE_QSORT_R)
- ADD_DEFINITIONS(-DHAVE_QSORT_R)
-ENDIF ()
-
-CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S)
-IF (HAVE_QSORT_S)
- ADD_DEFINITIONS(-DHAVE_QSORT_S)
-ENDIF ()
-
-IF( NOT CMAKE_CONFIGURATION_TYPES )
- # Build Debug by default
- IF (NOT CMAKE_BUILD_TYPE)
- SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
- ENDIF ()
-ELSE()
- # Using a multi-configuration generator eg MSVC or Xcode
- # that uses CMAKE_CONFIGURATION_TYPES and not CMAKE_BUILD_TYPE
-ENDIF()
-
-IF (SECURITY_FOUND)
- ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT)
- INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR})
-ENDIF ()
-
-IF (OPENSSL_FOUND)
- ADD_DEFINITIONS(-DGIT_OPENSSL)
- INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
- SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
-ENDIF()
-
-
-
-IF (THREADSAFE)
- IF (NOT WIN32)
- FIND_PACKAGE(Threads REQUIRED)
- ENDIF()
-
- ADD_DEFINITIONS(-DGIT_THREADS)
-ENDIF()
-
-IF (USE_NSEC)
- ADD_DEFINITIONS(-DGIT_USE_NSEC)
-ENDIF()
-
-IF (HAVE_STRUCT_STAT_ST_MTIM)
- ADD_DEFINITIONS(-DGIT_USE_STAT_MTIM)
-ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC)
- ADD_DEFINITIONS(-DGIT_USE_STAT_MTIMESPEC)
-ELSEIF (HAVE_STRUCT_STAT_ST_MTIME_NSEC)
- ADD_DEFINITIONS(-DGIT_USE_STAT_MTIME_NSEC)
-ENDIF()
-
-ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
-
-# Collect sourcefiles
-FILE(GLOB SRC_H include/git2.h include/git2/*.h include/git2/sys/*.h)
-
-# On Windows use specific platform sources
-IF (WIN32 AND NOT CYGWIN)
- ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501)
- FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h)
-ELSEIF (AMIGA)
- ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP)
-ELSE()
- IF (VALGRIND)
- ADD_DEFINITIONS(-DNO_MMAP)
- ENDIF()
- FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h)
-ENDIF()
-FILE(GLOB SRC_GIT2 src/*.c src/*.h src/transports/*.c src/transports/*.h src/xdiff/*.c src/xdiff/*.h)
-
-# Determine architecture of the machine
-IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
- ADD_DEFINITIONS(-DGIT_ARCH_64)
-ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4)
- ADD_DEFINITIONS(-DGIT_ARCH_32)
-ELSEIF (CMAKE_SIZEOF_VOID_P)
- MESSAGE(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)")
-ELSE()
- MESSAGE(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)")
-ENDIF()
-
-# Compile and link libgit2
-ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1} ${WIN_RC})
-TARGET_LINK_LIBRARIES(git2 ${SECURITY_DIRS})
-TARGET_LINK_LIBRARIES(git2 ${COREFOUNDATION_DIRS})
-TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
-TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
-TARGET_LINK_LIBRARIES(git2 ${GSSAPI_LIBRARIES})
-TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES})
-TARGET_OS_LIBRARIES(git2)
-
-# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
-# Win64+MSVC+static libs = linker error
-IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
- SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
-ENDIF()
-
-IDE_SPLIT_SOURCES(git2)
-
-IF (SONAME)
- SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
- SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_SOVERSION})
- IF (LIBGIT2_FILENAME)
- ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
- SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
- ELSEIF (DEFINED LIBGIT2_PREFIX)
- SET_TARGET_PROPERTIES(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
- ENDIF()
-ENDIF()
-STRING(REPLACE ";" " " LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS}")
-CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY)
-
-IF (MSVC_IDE)
- # Precompiled headers
- SET_TARGET_PROPERTIES(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
- SET_SOURCE_FILES_PROPERTIES(src/win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
-ENDIF ()
-
-# Install
-INSTALL(TARGETS git2
- RUNTIME DESTINATION ${BIN_INSTALL_DIR}
- LIBRARY DESTINATION ${LIB_INSTALL_DIR}
- ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
-)
-INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
-INSTALL(DIRECTORY include/git2 DESTINATION ${INCLUDE_INSTALL_DIR} )
-INSTALL(FILES include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
-
-# Tests
-IF (BUILD_CLAR)
- FIND_PACKAGE(PythonInterp)
-
- IF(NOT PYTHONINTERP_FOUND)
- MESSAGE(FATAL_ERROR "Could not find a python interpeter, which is needed to build the tests. "
- "Make sure python is available, or pass -DBUILD_CLAR=OFF to skip building the tests")
- ENDIF()
-
- SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources/")
- SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests")
- SET(CLAR_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources" CACHE PATH "Path to test resources.")
- ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
- ADD_DEFINITIONS(-DCLAR_RESOURCES=\"${TEST_RESOURCES}\")
- ADD_DEFINITIONS(-DCLAR_TMPDIR=\"libgit2_tests\")
-
- INCLUDE_DIRECTORIES(${CLAR_PATH})
- FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h)
- SET(SRC_CLAR "${CLAR_PATH}/main.c" "${CLAR_PATH}/clar_libgit2.c" "${CLAR_PATH}/clar_libgit2_trace.c" "${CLAR_PATH}/clar_libgit2_timer.c" "${CLAR_PATH}/clar.c")
-
- ADD_CUSTOM_COMMAND(
- OUTPUT ${CLAR_PATH}/clar.suite
- COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline -xstress .
- DEPENDS ${SRC_TEST}
- WORKING_DIRECTORY ${CLAR_PATH}
- )
-
- SET_SOURCE_FILES_PROPERTIES(
- ${CLAR_PATH}/clar.c
- PROPERTIES OBJECT_DEPENDS ${CLAR_PATH}/clar.suite)
-
- ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1})
-
- TARGET_LINK_LIBRARIES(libgit2_clar ${COREFOUNDATION_DIRS})
- TARGET_LINK_LIBRARIES(libgit2_clar ${SECURITY_DIRS})
- TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
- TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
- TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES})
- TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES})
- TARGET_OS_LIBRARIES(libgit2_clar)
- IDE_SPLIT_SOURCES(libgit2_clar)
-
- IF (MSVC_IDE)
- # Precompiled headers
- SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
- ENDIF ()
-
- ENABLE_TESTING()
- IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND)
- ADD_TEST(libgit2_clar libgit2_clar -ionline)
- ELSE ()
- ADD_TEST(libgit2_clar libgit2_clar -v)
- ENDIF ()
-
- # Add a test target which runs the cred callback tests, to be
- # called after setting the url and user
- ADD_TEST(libgit2_clar-cred_callback libgit2_clar -v -sonline::clone::cred_callback)
- ADD_TEST(libgit2_clar-proxy_credentials_in_url libgit2_clar -v -sonline::clone::proxy_credentials_in_url)
- ADD_TEST(libgit2_clar-proxy_credentials_request libgit2_clar -v -sonline::clone::proxy_credentials_request)
-ENDIF ()
-
-IF (TAGS)
- FIND_PROGRAM(CTAGS ctags)
- IF (NOT CTAGS)
- MESSAGE(FATAL_ERROR "Could not find ctags command")
- ENDIF ()
-
- FILE(GLOB_RECURSE SRC_ALL *.[ch])
-
- ADD_CUSTOM_COMMAND(
- OUTPUT tags
- COMMAND ${CTAGS} -a ${SRC_ALL}
- DEPENDS ${SRC_ALL}
- )
- ADD_CUSTOM_TARGET(
- do_tags ALL
- DEPENDS tags
- )
-ENDIF ()
-
-IF (BUILD_EXAMPLES)
- ADD_SUBDIRECTORY(examples)
-ENDIF ()
+++ /dev/null
-# Contributor Covenant Code of Conduct
-
-## Our Pledge
-
-In the interest of fostering an open and welcoming environment, we as
-contributors and maintainers pledge to making participation in our project and
-our community a harassment-free experience for everyone, regardless of age, body
-size, disability, ethnicity, gender identity and expression, level of experience,
-nationality, personal appearance, race, religion, or sexual identity and
-orientation.
-
-## Our Standards
-
-Examples of behavior that contributes to creating a positive environment
-include:
-
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
-
-Examples of unacceptable behavior by participants include:
-
-* The use of sexualized language or imagery and unwelcome sexual attention or
-advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic
- address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
- professional setting
-
-## Our Responsibilities
-
-Project maintainers are responsible for clarifying the standards of acceptable
-behavior and are expected to take appropriate and fair corrective action in
-response to any instances of unacceptable behavior.
-
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
-
-## Scope
-
-This Code of Conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community. Examples of
-representing a project or community include using an official project e-mail
-address, posting via an official social media account, or acting as an appointed
-representative at an online or offline event. Representation of a project may be
-further defined and clarified by project maintainers.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at [libgit2@gmail.com][email]. All
-complaints will be reviewed and investigated and will result in a response that
-is deemed necessary and appropriate to the circumstances. The project team is
-obligated to maintain confidentiality with regard to the reporter of an incident.
-Further details of specific enforcement policies may be posted separately.
-
-Project maintainers who do not follow or enforce the Code of Conduct in good
-faith may face temporary or permanent repercussions as determined by other
-members of the project's leadership.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
-available at [http://contributor-covenant.org/version/1/4][version]
-
-[email]: mailto:libgit2@gmail.com
-[homepage]: http://contributor-covenant.org
-[version]: http://contributor-covenant.org/version/1/4/
+++ /dev/null
-# Welcome to libgit2!
-
-We're making it easy to do interesting things with git, and we'd love to have
-your help.
-
-## Licensing
-
-By contributing to libgit2, you agree to release your contribution under
-the terms of the license. Except for the `examples` directory, all code
-is released under the [GPL v2 with linking exception](COPYING).
-
-The `examples` code is governed by the
-[CC0 Public Domain Dedication](examples/COPYING), so that you may copy
-from them into your own application.
-
-## Discussion & Chat
-
-We hang out in the
-[`#libgit2`](http://webchat.freenode.net/?channels=#libgit2)) channel on
-irc.freenode.net.
-
-Also, feel free to open an
-[Issue](https://github.com/libgit2/libgit2/issues/new) to start a discussion
-about any concerns you have. We like to use Issues for that so there is an
-easily accessible permanent record of the conversation.
-
-## Libgit2 Versions
-
-The `master` branch is the main branch where development happens.
-Releases are tagged
-(e.g. [v0.21.0](https://github.com/libgit2/libgit2/releases/tag/v0.21.0) )
-and when a critical bug fix needs to be backported, it will be done on a
-`<tag>-maint` maintenance branch.
-
-## Reporting Bugs
-
-First, know which version of libgit2 your problem is in and include it in
-your bug report. This can either be a tag (e.g.
-[v0.17.0](https://github.com/libgit2/libgit2/releases/tag/v0.17.0)) or a
-commit SHA
-(e.g. [01be7863](https://github.com/libgit2/libgit2/commit/01be7863)).
-Using [`git describe`](http://git-scm.com/docs/git-describe) is a
-great way to tell us what version you're working with.
-
-If you're not running against the latest `master` branch version,
-please compile and test against that to avoid re-reporting an issue that's
-already been fixed.
-
-It's *incredibly* helpful to be able to reproduce the problem. Please
-include a list of steps, a bit of code, and/or a zipped repository (if
-possible). Note that some of the libgit2 developers are employees of
-GitHub, so if your repository is private, find us on IRC and we'll figure
-out a way to help you.
-
-## Pull Requests
-
-Our work flow is a [typical GitHub
-flow](https://guides.github.com/introduction/flow/index.html), where
-contributors fork the [libgit2 repository](https://github.com/libgit2/libgit2),
-make their changes on branch, and submit a
-[Pull Request](https://help.github.com/articles/using-pull-requests)
-(a.k.a. "PR"). Pull requests should usually be targeted at the `master`
-branch.
-
-Life will be a lot easier for you (and us) if you follow this pattern
-(i.e. fork, named branch, submit PR). If you use your fork's `master`
-branch directly, things can get messy.
-
-Please include a nice description of your changes when you submit your PR;
-if we have to read the whole diff to figure out why you're contributing
-in the first place, you're less likely to get feedback and have your change
-merged in.
-
-If you are starting to work on a particular area, feel free to submit a PR
-that highlights your work in progress (and note in the PR title that it's
-not ready to merge). These early PRs are welcome and will help in getting
-visibility for your fix, allow others to comment early on the changes and
-also let others know that you are currently working on something.
-
-Before wrapping up a PR, you should be sure to:
-
-* Write tests to cover any functional changes
-* Update documentation for any changed public APIs
-* Add to the [`CHANGELOG.md`](CHANGELOG.md) file describing any major changes
-
-## Unit Tests
-
-We believe that our unit tests allow us to keep the quality of libgit2
-high: any new changes must not cause unit test failures, and new changes
-should include unit tests that cover the bug fixes or new features.
-For bug fixes, we prefer unit tests that illustrate the failure before
-the change, but pass with your changes.
-
-In addition to new tests, please ensure that your changes do not cause
-any other test failures. Running the entire test suite is helpful
-before you submit a pull request. When you build libgit2, the test
-suite will also be built. You can run most of the tests by simply running
-the resultant `libgit2_clar` binary. If you want to run a specific
-unit test, you can name it with the `-s` option. For example:
-
- libgit2_clar -sstatus::worktree::long_filenames
-
-Or you can run an entire class of tests. For example, to run all the
-worktree status tests:
-
- libgit2_clar -sstatus::worktree
-
-The default test run is fairly exhaustive, but it will exclude some
-unit tests by default: in particular, those that talk to network
-servers and the tests that manipulate the filesystem in onerous
-ways (and may need to have special privileges to run). To run the
-network tests:
-
- libgit2_clar -ionline
-
-In addition, various tests may be enabled by environment variables,
-like the ones that write exceptionally large repositories or manipulate
-the filesystem structure in unexpected ways. These tests *may be
-dangerous* to run on a normal machine and may harm your filesystem. It's
-not recommended that you run these; instead, the continuous integration
-servers will run these (in a sandbox).
-
-## Porting Code From Other Open-Source Projects
-
-`libgit2` is licensed under the terms of the GPL v2 with a linking
-exception. Any code brought in must be compatible with those terms.
-
-The most common case is porting code from core Git. Git is a pure GPL
-project, which means that in order to port code to this project, we need the
-explicit permission of the author. Check the
-[`git.git-authors`](https://github.com/libgit2/libgit2/blob/development/git.git-authors)
-file for authors who have already consented.
-
-Other licenses have other requirements; check the license of the library
-you're porting code *from* to see what you need to do. As a general rule,
-MIT and BSD (3-clause) licenses are typically no problem. Apache 2.0
-license typically doesn't work due to GPL incompatibility.
-
-If your pull request uses code from core Git, another project, or code
-from a forum / Stack Overflow, then *please* flag this in your PR and make
-sure you've given proper credit to the original author in the code
-snippet.
-
-## Style Guide
-
-The public API of `libgit2` is [ANSI C](http://en.wikipedia.org/wiki/ANSI_C)
-(a.k.a. C89) compatible. Internally, `libgit2` is written using a portable
-subset of C99 - in order to compile with GCC, Clang, MSVC, etc., we keep
-local variable declarations at the tops of blocks only and avoid `//` style
-comments. Additionally, `libgit2` follows some extra conventions for
-function and type naming, code formatting, and testing.
-
-We like to keep the source code consistent and easy to read. Maintaining
-this takes some discipline, but it's been more than worth it. Take a look
-at the [conventions
-file](https://github.com/libgit2/libgit2/blob/development/CONVENTIONS.md).
-
-## Starter Projects
-
-See our [projects
-list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md).
+++ /dev/null
-# Libgit2 Conventions
-
-We like to keep the source consistent and readable. Herein are some
-guidelines that should help with that.
-
-## External API
-
-We have a few rules to avoid surprising ways of calling functions and
-some rules for consumers of the library to avoid stepping on each
-other's toes.
-
- - Property accessors return the value directly (e.g. an `int` or
- `const char *`) but if a function can fail, we return a `int` value
- and the output parameters go first in the parameter list, followed
- by the object that a function is operating on, and then any other
- arguments the function may need.
-
- - If a function returns an object as a return value, that function is
- a getter and the object's lifetime is tied to the parent
- object. Objects which are returned as the first argument as a
- pointer-to-pointer are owned by the caller and it is repsponsible
- for freeing it. Strings are returned via `git_buf` in order to
- allow for re-use and safe freeing.
-
- - Most of what libgit2 does relates to I/O so you as a general rule
- you should assume that any function can fail due to errors as even
- getting data from the filesystem can result in all sorts of errors
- and complex failure cases.
-
- - Paths inside the Git system are separated by a slash (0x2F). If a
- function accepts a path on disk, then backslashes (0x5C) are also
- accepted on Windows.
-
- - Do not mix allocators. If something has been allocated by libgit2,
- you do not know which is the right free function in the general
- case. Use the free functions provided for each object type.
-
-## Compatibility
-
-`libgit2` runs on many different platforms with many different compilers.
-
-The public API of `libgit2` is [ANSI C](http://en.wikipedia.org/wiki/ANSI_C)
-(a.k.a. C89) compatible.
-
-Internally, `libgit2` is written using a portable subset of C99 - in order
-to maximize compatibility (e.g. with MSVC) we avoid certain C99
-extensions. Specifically, we keep local variable declarations at the tops
-of blocks only and we avoid `//` style comments.
-
-Also, to the greatest extent possible, we try to avoid lots of `#ifdef`s
-inside the core code base. This is somewhat unavoidable, but since it can
-really hamper maintainability, we keep it to a minimum.
-
-## Match Surrounding Code
-
-If there is one rule to take away from this document, it is *new code should
-match the surrounding code in a way that makes it impossible to distinguish
-the new from the old.* Consistency is more important to us than anyone's
-personal opinion about where braces should be placed or spaces vs. tabs.
-
-If a section of code is being completely rewritten, it is okay to bring it
-in line with the standards that are laid out here, but we will not accept
-submissions that contain a large number of changes that are merely
-reformatting.
-
-## Naming Things
-
-All external types and functions start with `git_` and all `#define` macros
-start with `GIT_`. The `libgit2` API is mostly broken into related
-functional modules each with a corresponding header. All functions in a
-module should be named like `git_modulename_functioname()`
-(e.g. `git_repository_open()`).
-
-Functions with a single output parameter should name that parameter `out`.
-Multiple outputs should be named `foo_out`, `bar_out`, etc.
-
-Parameters of type `git_oid` should be named `id`, or `foo_id`. Calls that
-return an OID should be named `git_foo_id`.
-
-Where a callback function is used, the function should also include a
-user-supplied extra input that is a `void *` named "payload" that will be
-passed through to the callback at each invocation.
-
-## Typedefs
-
-Wherever possible, use `typedef`. In some cases, if a structure is just a
-collection of function pointers, the pointer types don't need to be
-separately typedef'd, but loose function pointer types should be.
-
-## Exports
-
-All exported functions must be declared as:
-
-```c
-GIT_EXTERN(result_type) git_modulename_functionname(arg_list);
-```
-
-## Internals
-
-Functions whose *modulename* is followed by two underscores,
-for example `git_odb__read_packed`, are semi-private functions.
-They are primarily intended for use within the library itself,
-and may disappear or change their signature in a future release.
-
-## Parameters
-
-Out parameters come first.
-
-Whenever possible, pass argument pointers as `const`. Some structures (such
-as `git_repository` and `git_index`) have mutable internal structure that
-prevents this.
-
-Callbacks should always take a `void *` payload as their last parameter.
-Callback pointers are grouped with their payloads, and typically come last
-when passed as arguments:
-
-```c
-int git_foo(git_repository *repo, git_foo_cb callback, void *payload);
-```
-
-## Memory Ownership
-
-Some APIs allocate memory which the caller is responsible for freeing; others
-return a pointer into a buffer that's owned by some other object. Make this
-explicit in the documentation.
-
-## Return codes
-
-Most public APIs should return an `int` error code. As is typical with most
-C library functions, a zero value indicates success and a negative value
-indicates failure.
-
-Some bindings will transform these returned error codes into exception
-types, so returning a semantically appropriate error code is important.
-Check
-[`include/git2/errors.h`](https://github.com/libgit2/libgit2/blob/development/include/git2/errors.h)
-for the return codes already defined.
-
-In your implementation, use `giterr_set()` to provide extended error
-information to callers.
-
-If a `libgit2` function internally invokes another function that reports an
-error, but the error is not propagated up, use `giterr_clear()` to prevent
-callers from getting the wrong error message later on.
-
-
-## Structs
-
-Most public types should be opaque, e.g.:
-
-```C
-typedef struct git_odb git_odb;
-```
-
-...with allocation functions returning an "instance" created within
-the library, and not within the application. This allows the type
-to grow (or shrink) in size without rebuilding client code.
-
-To preserve ABI compatibility, include an `int version` field in all opaque
-structures, and initialize to the latest version in the construction call.
-Increment the "latest" version whenever the structure changes, and try to only
-append to the end of the structure.
-
-## Option Structures
-
-If a function's parameter count is too high, it may be desirable to package
-up the options in a structure. Make them transparent, include a version
-field, and provide an initializer constant or constructor. Using these
-structures should be this easy:
-
-```C
-git_foo_options opts = GIT_FOO_OPTIONS_INIT;
-opts.baz = BAZ_OPTION_ONE;
-git_foo(&opts);
-```
-
-## Enumerations
-
-Typedef all enumerated types. If each option stands alone, use the enum
-type for passing them as parameters; if they are flags to be OR'ed together,
-pass them as `unsigned int` or `uint32_t` or some appropriate type.
-
-## Code Layout
-
-Try to keep lines less than 80 characters long. This is a loose
-requirement, but going significantly over 80 columns is not nice.
-
-Use common sense to wrap most code lines; public function declarations
-can use a couple of different styles:
-
-```c
-/** All on one line is okay if it fits */
-GIT_EXTERN(int) git_foo_simple(git_oid *id);
-
-/** Otherwise one argument per line is a good next step */
-GIT_EXTERN(int) git_foo_id(
- git_oid **out,
- int a,
- int b);
-```
-
-Indent with tabs; set your editor's tab width to 4 for best effect.
-
-Avoid trailing whitespace and only commit Unix-style newlines (i.e. no CRLF
-in the repository - just set `core.autocrlf` to true if you are writing code
-on a Windows machine).
-
-## Documentation
-
-All comments should conform to Doxygen "javadoc" style conventions for
-formatting the public API documentation. Try to document every parameter,
-and keep the comments up to date if you change the parameter list.
-
-## Public Header Template
-
-Use this template when creating a new public header.
-
-```C
-#ifndef INCLUDE_git_${filename}_h__
-#define INCLUDE_git_${filename}_h__
-
-#include "git/common.h"
-
-/**
- * @file git/${filename}.h
- * @brief Git some description
- * @defgroup git_${filename} some description routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/* ... definitions ... */
-
-/** @} */
-GIT_END_DECL
-#endif
-```
-
-## Inlined functions
-
-All inlined functions must be declared as:
-
-```C
-GIT_INLINE(result_type) git_modulename_functionname(arg_list);
-```
-
-`GIT_INLINE` (or `inline`) should not be used in public headers in order
-to preserve ANSI C compatibility.
-
-## Tests
-
-`libgit2` uses the [clar](https://github.com/vmg/clar) testing framework.
-
-All PRs should have corresponding tests.
-
-* If the PR fixes an existing issue, the test should fail prior to applying
- the PR and succeed after applying it.
-* If the PR is for new functionality, then the tests should exercise that
- new functionality to a certain extent. We don't require 100% coverage
- right now (although we are getting stricter over time).
-
-When adding new tests, we prefer if you attempt to reuse existing test data
-(in `tests-clar/resources/`) if possible. If you are going to add new test
-repositories, please try to strip them of unnecessary files (e.g. sample
-hooks, etc).
+++ /dev/null
- libgit2 is Copyright (C) the libgit2 contributors,
- unless otherwise stated. See the AUTHORS file for details.
-
- Note that the only valid version of the GPL as far as this project
- is concerned is _this_ particular version of the license (ie v2, not
- v2.2 or v3.x or whatever), unless explicitly otherwise stated.
-
-----------------------------------------------------------------------
-
- LINKING EXCEPTION
-
- In addition to the permissions in the GNU General Public License,
- the authors give you unlimited permission to link the compiled
- version of this library into combinations with other programs,
- and to distribute those combinations without any restriction
- coming from the use of this file. (The General Public License
- restrictions do apply in other respects; for example, they cover
- modification of the file, and distribution when not linked into
- a combined executable.)
-
-----------------------------------------------------------------------
-
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
-
-----------------------------------------------------------------------
-
-The bundled ZLib code is licensed under the ZLib license:
-
-Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-
- Jean-loup Gailly Mark Adler
- jloup@gzip.org madler@alumni.caltech.edu
-
-----------------------------------------------------------------------
-
-The Clar framework is licensed under the ISC license:
-
-Copyright (c) 2011-2015 Vicent Marti
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-----------------------------------------------------------------------
-
-The regex library (deps/regex/) is licensed under the GNU LGPL
-(available at the end of this file).
-
-Definitions for data structures and routines for the regular
-expression library.
-
-Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008
-Free Software Foundation, Inc.
-This file is part of the GNU C Library.
-
-The GNU C Library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
-License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version.
-
-The GNU C Library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with the GNU C Library; if not, write to the Free
-Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-----------------------------------------------------------------------
-
-The bundled winhttp definition files (deps/winhttp/) are licensed under
-the GNU LGPL (available at the end of this file).
-
-Copyright (C) 2007 Francois Gouget
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
-License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
-
-----------------------------------------------------------------------
-
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL. It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
- This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it. You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
- When we speak of free software, we are referring to freedom of use,
-not price. Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
- To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights. These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
- For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you. You must make sure that they, too, receive or can get the source
-code. If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it. And you must show them these terms so they know their rights.
-
- We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
- To protect each distributor, we want to make it very clear that
-there is no warranty for the free library. Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-\f
- Finally, software patents pose a constant threat to the existence of
-any free program. We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder. Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
- Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License. This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License. We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
- When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library. The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom. The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
- We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License. It also provides other free software developers Less
-of an advantage over competing non-free programs. These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries. However, the Lesser license provides advantages in certain
-special circumstances.
-
- For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard. To achieve this, non-free programs must be
-allowed to use the library. A more frequent case is that a free
-library does the same job as widely used non-free libraries. In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
- In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software. For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
- Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
- The precise terms and conditions for copying, distribution and
-modification follow. Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library". The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-\f
- GNU LESSER GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
- A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
- The "Library", below, refers to any such software library or work
-which has been distributed under these terms. A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language. (Hereinafter, translation is
-included without limitation in the term "modification".)
-
- "Source code" for a work means the preferred form of the work for
-making modifications to it. For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
-
- Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it). Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-
- 1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
- You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-\f
- 2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) The modified work must itself be a software library.
-
- b) You must cause the files modified to carry prominent notices
- stating that you changed the files and the date of any change.
-
- c) You must cause the whole of the work to be licensed at no
- charge to all third parties under the terms of this License.
-
- d) If a facility in the modified Library refers to a function or a
- table of data to be supplied by an application program that uses
- the facility, other than as an argument passed when the facility
- is invoked, then you must make a good faith effort to ensure that,
- in the event an application does not supply such function or
- table, the facility still operates, and performs whatever part of
- its purpose remains meaningful.
-
- (For example, a function in a library to compute square roots has
- a purpose that is entirely well-defined independent of the
- application. Therefore, Subsection 2d requires that any
- application-supplied function or table used by this function must
- be optional: if the application does not supply it, the square
- root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library. To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License. (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.) Do not make any other change in
-these notices.
-\f
- Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
- This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
- 4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
- If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library". Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
- However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library". The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
- When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library. The
-threshold for this to be true is not precisely defined by law.
-
- If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work. (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
- Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-\f
- 6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
- You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License. You must supply a copy of this License. If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License. Also, you must do one
-of these things:
-
- a) Accompany the work with the complete corresponding
- machine-readable source code for the Library including whatever
- changes were used in the work (which must be distributed under
- Sections 1 and 2 above); and, if the work is an executable linked
- with the Library, with the complete machine-readable "work that
- uses the Library", as object code and/or source code, so that the
- user can modify the Library and then relink to produce a modified
- executable containing the modified Library. (It is understood
- that the user who changes the contents of definitions files in the
- Library will not necessarily be able to recompile the application
- to use the modified definitions.)
-
- b) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (1) uses at run time a
- copy of the library already present on the user's computer system,
- rather than copying library functions into the executable, and (2)
- will operate properly with a modified version of the library, if
- the user installs one, as long as the modified version is
- interface-compatible with the version that the work was made with.
-
- c) Accompany the work with a written offer, valid for at
- least three years, to give the same user the materials
- specified in Subsection 6a, above, for a charge no more
- than the cost of performing this distribution.
-
- d) If distribution of the work is made by offering access to copy
- from a designated place, offer equivalent access to copy the above
- specified materials from the same place.
-
- e) Verify that the user has already received a copy of these
- materials or that you have already sent this user a copy.
-
- For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it. However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
- It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system. Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-\f
- 7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
- a) Accompany the combined library with a copy of the same work
- based on the Library, uncombined with any other library
- facilities. This must be distributed under the terms of the
- Sections above.
-
- b) Give prominent notice with the combined library of the fact
- that part of it is a work based on the Library, and explaining
- where to find the accompanying uncombined form of the same work.
-
- 8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License. Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License. However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
- 9. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Library or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
- 10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-\f
- 11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all. For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded. In such case, this License incorporates the limitation as if
-written in the body of this License.
-
- 13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation. If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-\f
- 14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission. For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this. Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
- NO WARRANTY
-
- 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
- END OF TERMS AND CONDITIONS
-\f
- How to Apply These Terms to Your New Libraries
-
- If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change. You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
- To apply these terms, attach the following notices to the library. It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
- <one line to give the library's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the
- library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
- <signature of Ty Coon>, 1 April 1990
- Ty Coon, President of Vice
-
-That's all there is to it!
-
-----------------------------------------------------------------------
+++ /dev/null
-Projects For LibGit2
-====================
-
-So, you want to start helping out with `libgit2`? That's fantastic! We
-welcome contributions and we promise we'll try to be nice.
-
-This is a list of libgit2 related projects that new contributors can take
-on. It includes a number of good starter projects as well as some larger
-ideas that no one is actively working on.
-
-## Before You Start
-
-Please start by reading the [README.md](README.md),
-[CONTRIBUTING.md](CONTRIBUTING.md), and [CONVENTIONS.md](CONVENTIONS.md)
-files before diving into one of these projects. Those explain our work
-flow and coding conventions to help ensure that your work will be easily
-integrated into libgit2.
-
-Next, work through the build instructions and make sure you can clone the
-repository, compile it, and run the tests successfully. That will make
-sure that your development environment is set up correctly and you are
-ready to start on libgit2 development.
-
-## Starter Projects
-
-These are good small projects to get started with libgit2.
-
-* Look at the `examples/` programs, find an existing one that mirrors a
- core Git command and add a missing command-line option. There are many
- gaps right now and this helps demonstrate how to use the library. Here
- are some specific ideas (though there are many more):
- * Fix the `examples/diff.c` implementation of the `-B`
- (a.k.a. `--break-rewrites`) command line option to actually look for
- the optional `[<n>][/<m>]` configuration values. There is an
- existing comment that reads `/* TODO: parse thresholds */`. The
- trick to this one will be doing it in a manner that is clean and
- simple, but still handles the various cases correctly (e.g. `-B/70%`
- is apparently a legal setting).
- * Implement the `--log-size` option for `examples/log.c`. I think all
- the data is available, you would just need to add the code into the
- `print_commit()` routine (along with a way of passing the option
- into that function).
- * As an extension to the matching idea for `examples/log.c`, add the
- `-i` option to use `strcasestr()` for matches.
- * For `examples/log.c`, implement the `--first-parent` option now that
- libgit2 supports it in the revwalk API.
-* Pick a Git command that is not already emulated in `examples/` and write
- a new example that mirrors the behavior. Examples don't have to be
- perfect emulations, but should demonstrate how to use the libgit2 APIs
- to get results that are similar to Git commands. This lets you (and us)
- easily exercise a particular facet of the API and measure compatibility
- and feature parity with core git.
-* Submit a PR to clarify documentation! While we do try to document all of
- the APIs, your fresh eyes on the documentation will find areas that are
- confusing much more easily.
-
-If none of these appeal to you, take a look at our issues list to see if
-there are any unresolved issues you'd like to jump in on.
-
-## Larger Projects
-
-These are ideas for larger projects mostly taken from our backlog of
-[Issues](https://github.com/libgit2/libgit2/issues). Please don't dive
-into one of these as a first project for libgit2 - we'd rather get to
-know you first by successfully shipping your work on one of the smaller
-projects above.
-
-Some of these projects are broken down into subprojects and/or have
-some incremental steps listed towards the larger goal. Those steps
-might make good smaller projects by themselves.
-
-* Port part of the Git test suite to run against the command line emulation
- in `examples/`
- * Pick a Git command that is emulated in our `examples/` area
- * Extract the Git tests that exercise that command
- * Convert the tests to call our emulation
- * These tests could go in `examples/tests/`...
-* Add hooks API to enumerate and manage hooks (not run them at this point)
- * Enumeration of available hooks
- * Lookup API to see which hooks have a script and get the script
- * Read/write API to load a hook script and write a hook script
- * Eventually, callback API to invoke a hook callback when libgit2
- executes the action in question
-* Isolate logic of ignore evaluation into a standalone API
-* Upgrade internal libxdiff code to latest from core Git
-* Tree builder improvements:
- * Extend to allow building a tree hierarchy
-* Apply-patch API
-* Add a patch editing API to enable "git add -p" type operations
-* Textconv API to filter binary data before generating diffs (something
- like the current Filter API, probably).
-* Performance profiling and improvement
-* Support "git replace" ref replacements
-* Include conflicts in diff results and in status
- * GIT_DELTA_CONFLICT for items in conflict (with multiple files)
- * Appropriate flags for status
-* Support sparse checkout (i.e. "core.sparsecheckout" and ".git/info/sparse-checkout")
+++ /dev/null
-libgit2 - the Git linkable library
-==================================
-
-[](http://travis-ci.org/libgit2/libgit2)
-[](https://ci.appveyor.com/project/libgit2/libgit2/branch/master)
-[](https://scan.coverity.com/projects/639)
-
-`libgit2` is a portable, pure C implementation of the Git core methods
-provided as a re-entrant linkable library with a solid API, allowing you to
-write native speed custom Git applications in any language with bindings.
-
-`libgit2` is licensed under a **very permissive license** (GPLv2 with a special
-Linking Exception). This basically means that you can link it (unmodified)
-with any kind of software without having to release its source code.
-Additionally, the example code has been released to the public domain (see the
-[separate license](examples/COPYING) for more information).
-
-Getting Help
-============
-
-**Join us on Slack**
-
-Visit [slack.libgit2.org](http://slack.libgit2.org/) to sign up, then join
-us in `#libgit2`. If you prefer IRC, you can also point your client to our
-slack channel once you've registered.
-
-**Getting Help**
-
-If you have questions about the library, please be sure to check out the
-[API documentation](http://libgit2.github.com/libgit2/). If you still have
-questions, reach out to us on Slack or post a question on
-[StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) (with the `libgit2` tag).
-
-**Reporting Bugs**
-
-Please open a [GitHub Issue](https://github.com/libgit2/libgit2/issues) and
-include as much information as possible. If possible, provide sample code
-that illustrates the problem you're seeing. If you're seeing a bug only
-on a specific repository, please provide a link to it if possible.
-
-We ask that you not open a GitHub Issue for help, only for bug reports.
-
-What It Can Do
-==============
-
-The goal of this library is to allow its users the ability to handle Git data in
-their applications from their programming language of choice, as is used in
-production for many applications including the GitHub.com site, in Plastic SCM
-and also powering Microsoft's Visual Studio tools for Git.
-
-It does not aim to replace the git tool or its user-facing commands. Some APIs
-resemble the plumbing commands as those align closely with the concepts of the
-Git system, but most commands a user would type are out of scope for this
-library to implement directly.
-
-The library provides:
-
-* SHA conversions, formatting and shortening
-* abstracted ODB backend system
-* commit, tag, tree and blob parsing, editing, and write-back
-* tree traversal
-* revision walking
-* index file (staging area) manipulation
-* reference management (including packed references)
-* config file management
-* high level repository management
-* thread safety and reentrancy
-* descriptive and detailed error messages
-* ...and more (over 175 different API calls)
-
-Optional dependencies
-=====================
-
-While the library provides git functionality without the need for
-dependencies, it can make use of a few libraries to add to it:
-
-- pthreads (non-Windows) to enable threadsafe access as well as multi-threaded pack generation
-- OpenSSL (non-Windows) to talk over HTTPS and provide the SHA-1 functions
-- LibSSH2 to enable the SSH transport
-- iconv (OSX) to handle the HFS+ path encoding peculiarities
-
-Initialization
-===============
-
-The library needs to keep track of some global state. Call
-
- git_libgit2_init();
-
-before calling any other libgit2 functions. You can call this function many times. A matching number of calls to
-
- git_libgit2_shutdown();
-
-will free the resources. Note that if you have worker threads, you should
-call `git_libgit2_shutdown` *after* those threads have exited. If you
-require assistance coordinating this, simply have the worker threads call
-`git_libgit2_init` at startup and `git_libgit2_shutdown` at shutdown.
-
-Threading
-=========
-
-See [THREADING](THREADING.md) for information
-
-Conventions
-===========
-
-See [CONVENTIONS](CONVENTIONS.md) for an overview of the external
-and internal API/coding conventions we use.
-
-Building libgit2 - Using CMake
-==============================
-
-`libgit2` builds cleanly on most platforms without any external dependencies.
-Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthreads` to be available;
-they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
-for threading.
-
-The `libgit2` library is built using [CMake](<https://cmake.org/>) (version 2.8 or newer) on all platforms.
-
-On most systems you can build the library using the following commands
-
- $ mkdir build && cd build
- $ cmake ..
- $ cmake --build .
-
-Alternatively you can point the CMake GUI tool to the CMakeLists.txt file and generate platform specific build project or IDE workspace.
-
-To install the library you can specify the install prefix by setting:
-
- $ cmake .. -DCMAKE_INSTALL_PREFIX=/install/prefix
- $ cmake --build . --target install
-
-For more advanced use or questions about CMake please read <https://cmake.org/Wiki/CMake_FAQ>.
-
-The following CMake variables are declared:
-
-- `BIN_INSTALL_DIR`: Where to install binaries to.
-- `LIB_INSTALL_DIR`: Where to install libraries to.
-- `INCLUDE_INSTALL_DIR`: Where to install headers to.
-- `BUILD_SHARED_LIBS`: Build libgit2 as a Shared Library (defaults to ON)
-- `BUILD_CLAR`: Build [Clar](https://github.com/vmg/clar)-based test suite (defaults to ON)
-- `THREADSAFE`: Build libgit2 with threading support (defaults to ON)
-- `STDCALL`: Build libgit2 as `stdcall`. Turn off for `cdecl` (Windows; defaults to ON)
-
-Compiler and linker options
----------------------------
-
-CMake lets you specify a few variables to control the behavior of the
-compiler and linker. These flags are rarely used but can be useful for
-64-bit to 32-bit cross-compilation.
-
-- `CMAKE_C_FLAGS`: Set your own compiler flags
-- `CMAKE_FIND_ROOT_PATH`: Override the search path for libraries
-- `ZLIB_LIBRARY`, `OPENSSL_SSL_LIBRARY` AND `OPENSSL_CRYPTO_LIBRARY`:
-Tell CMake where to find those specific libraries
-
-MacOS X
--------
-
-If you want to build a universal binary for Mac OS X, CMake sets it
-all up for you if you use `-DCMAKE_OSX_ARCHITECTURES="i386;x86_64"`
-when configuring.
-
-Windows
--------
-
-You need to run the CMake commands from the Visual Studio command
-prompt, not the regular or Windows SDK one. Select the right generator
-for your version with the `-G "Visual Studio X" option.
-
-See [the website](http://libgit2.github.com/docs/guides/build-and-link/)
-for more detailed instructions.
-
-Android
--------
-
-Extract toolchain from NDK using, `make-standalone-toolchain.sh` script.
-Optionally, crosscompile and install OpenSSL inside of it. Then create CMake
-toolchain file that configures paths to your crosscompiler (substitute `{PATH}`
-with full path to the toolchain):
-
- SET(CMAKE_SYSTEM_NAME Linux)
- SET(CMAKE_SYSTEM_VERSION Android)
-
- SET(CMAKE_C_COMPILER {PATH}/bin/arm-linux-androideabi-gcc)
- SET(CMAKE_CXX_COMPILER {PATH}/bin/arm-linux-androideabi-g++)
- SET(CMAKE_FIND_ROOT_PATH {PATH}/sysroot/)
-
- SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
- SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
- SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-
-Add `-DCMAKE_TOOLCHAIN_FILE={pathToToolchainFile}` to cmake command
-when configuring.
-
-Language Bindings
-==================================
-
-Here are the bindings to libgit2 that are currently available:
-
-* C++
- * libqgit2, Qt bindings <https://projects.kde.org/projects/playground/libs/libqgit2/repository/>
-* Chicken Scheme
- * chicken-git <https://wiki.call-cc.org/egg/git>
-* D
- * dlibgit <https://github.com/s-ludwig/dlibgit>
-* Delphi
- * GitForDelphi <https://github.com/libgit2/GitForDelphi>
-* Erlang
- * Geef <https://github.com/carlosmn/geef>
-* Go
- * git2go <https://github.com/libgit2/git2go>
-* GObject
- * libgit2-glib <https://wiki.gnome.org/Projects/Libgit2-glib>
-* Haskell
- * hgit2 <https://github.com/jwiegley/gitlib>
-* Java
- * Jagged <https://github.com/ethomson/jagged>
-* Julia
- * LibGit2.jl <https://github.com/jakebolewski/LibGit2.jl>
-* Lua
- * luagit2 <https://github.com/libgit2/luagit2>
-* .NET
- * libgit2sharp <https://github.com/libgit2/libgit2sharp>
-* Node.js
- * nodegit <https://github.com/nodegit/nodegit>
-* Objective-C
- * objective-git <https://github.com/libgit2/objective-git>
-* OCaml
- * ocaml-libgit2 <https://github.com/fxfactorial/ocaml-libgit2>
-* Parrot Virtual Machine
- * parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
-* Perl
- * Git-Raw <https://github.com/jacquesg/p5-Git-Raw>
-* PHP
- * php-git <https://github.com/libgit2/php-git>
-* PowerShell
- * PSGit <https://github.com/PoshCode/PSGit>
-* Python
- * pygit2 <https://github.com/libgit2/pygit2>
-* R
- * git2r <https://github.com/ropensci/git2r>
-* Ruby
- * Rugged <https://github.com/libgit2/rugged>
-* Rust
- * git2-rs <https://github.com/alexcrichton/git2-rs>
-* Swift
- * SwiftGit2 <https://github.com/SwiftGit2/SwiftGit2>
-* Vala
- * libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
-
-If you start another language binding to libgit2, please let us know so
-we can add it to the list.
-
-How Can I Contribute?
-==================================
-
-We welcome new contributors! We have a number of issues marked as
-["up for grabs"](https://github.com/libgit2/libgit2/issues?q=is%3Aissue+is%3Aopen+label%3A%22up+for+grabs%22)
-and
-["easy fix"](https://github.com/libgit2/libgit2/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3A%22easy+fix%22)
-that are good places to jump in and get started. There's much more detailed
-information in our list of [outstanding projects](PROJECTS.md).
-
-Please be sure to check the [contribution guidelines](CONTRIBUTING.md) to
-understand our workflow, and the libgit2 [coding conventions](CONVENTIONS.md).
-
-License
-==================================
-
-`libgit2` is under GPL2 **with linking exception**. This means you can link to
-and use the library from any program, proprietary or open source; paid or
-gratis. However, if you modify libgit2 itself, you must distribute the
-source to your modified version of libgit2.
-
-See the [COPYING file](COPYING) for the full license text.
+++ /dev/null
-Threads in libgit2
-==================
-
-You may safely use any libgit2 object from any thread, though there
-may be issues depending on the cryptographic libraries libgit2 or its
-dependencies link to (more on this later). For libgit2 itself,
-provided you take the following into consideration you won't run into
-issues:
-
-Sharing objects
----------------
-
-Use an object from a single thread at a time. Most data structures do
-not guard against concurrent access themselves. This is because they
-are rarely used in isolation and it makes more sense to synchronize
-access via a larger lock or similar mechanism.
-
-There are some objects which are read-only/immutable and are thus safe
-to share across threads, such as references and configuration
-snapshots.
-
-Error messages
---------------
-
-The error message is thread-local. The `giterr_last()` call must
-happen on the same thread as the error in order to get the
-message. Often this will be the case regardless, but if you use
-something like the [GCD](http://en.wikipedia.org/wiki/Grand_Central_Dispatch)
-on Mac OS X (where code is executed on an arbitrary thread), the code
-must make sure to retrieve the error code on the thread where the error
-happened.
-
-Threads and cryptographic libraries
-=======================================
-
-On Windows
-----------
-
-When built as a native Windows DLL, libgit2 uses WinCNG and WinHTTP,
-both of which are thread-safe. You do not need to do anything special.
-
-When using libssh2 which itself uses WinCNG, there are no special
-steps necessary. If you are using a MinGW or similar environment where
-libssh2 uses OpenSSL or libgcrypt, then the general case affects
-you.
-
-On Mac OS X
------------
-
-By default we use libcurl to perform the encryption. The
-system-provided libcurl uses SecureTransport, so no special steps are
-necessary. If you link against another libcurl (e.g. from homebrew)
-refer to the general case.
-
-If the option to use libcurl was deactivated, the library makes use of
-CommonCrypto and SecureTransport for cryptographic support. These are
-thread-safe and you do not need to do anything special.
-
-Note that libssh2 may still use OpenSSL itself. In that case, the
-general case still affects you if you use ssh.
-
-General Case
-------------
-
-If it's available, by default we use libcurl to provide HTTP tunneling support,
-which may be linked against a number of cryptographic libraries and has its
-own
-[recommendations for thread safety](https://curl.haxx.se/libcurl/c/threadsafe.html).
-
-If there are no alternative TLS implementations (currently only
-SecureTransport), libgit2 uses OpenSSL in order to use HTTPS as a transport.
-OpenSSL is thread-safe starting at version 1.1.0. If your copy of libgit2 is
-linked against that version, you do not need to take any further steps.
-
-Older versions of OpenSSL are made to be thread-implementation agnostic, and the
-users of the library must set which locking function it should use. libgit2
-cannot know what to set as the user of libgit2 may also be using OpenSSL independently and
-the locking settings must then live outside the lifetime of libgit2.
-
-Even if libgit2 doesn't use OpenSSL directly, OpenSSL can still be used by
-libssh2 or libcurl depending on the configuration. If OpenSSL is used by
-more than one library, you only need to set up threading for OpenSSL once.
-
-If libgit2 is linked against OpenSSL, it provides a last-resort convenience function
-`git_openssl_set_locking()` (available in `sys/openssl.h`) to use the
-platform-native mutex mechanisms to perform the locking, which you can use
-if you do not want to use OpenSSL outside of libgit2, or you
-know that libgit2 will outlive the rest of the operations. It is then not
-safe to use OpenSSL multi-threaded after libgit2's shutdown function
-has been called. Note `git_openssl_set_locking()` only works if
-libgit2 uses OpenSSL directly - if OpenSSL is only used as a dependency
-of libssh2 or libcurl as described above, `git_openssl_set_locking()` is a no-op.
-
-If your programming language offers a package/bindings for OpenSSL,
-you should very strongly prefer to use that in order to set up
-locking, as they provide a level of coordination which is impossible
-when using this function.
-
-See the
-[OpenSSL documentation](https://www.openssl.org/docs/crypto/threads.html)
-on threading for more details, and http://trac.libssh2.org/wiki/MultiThreading
-for a specific example of providing the threading callbacks.
-
-libssh2 may be linked against OpenSSL or libgcrypt. If it uses OpenSSL,
-see the above paragraphs. If it uses libgcrypt, then you need to
-set up its locking before using it multi-threaded. libgit2 has no
-direct connection to libgcrypt and thus has no convenience functions for
-it (but libgcrypt has macros). Read libgcrypt's
-[threading documentation for more information](http://www.gnupg.org/documentation/manuals/gcrypt/Multi_002dThreading.html)
-
-It is your responsibility as an application author or packager to know
-what your dependencies are linked against and to take the appropriate
-steps to ensure the cryptographic libraries are thread-safe. We agree
-that this situation is far from ideal but at this time it is something
-the application authors need to deal with.
+++ /dev/null
-{
- "name": "libgit2",
- "github": "libgit2/libgit2",
- "input": "include/git2",
- "prefix": "git_",
- "output": "docs",
- "branch": "gh-pages",
- "examples": "examples",
- "legacy": {
- "input": {"src/git": ["v0.1.0"],
- "src/git2": ["v0.2.0", "v0.3.0"]}
- }
-}
+++ /dev/null
-version: '{build}'
-branches:
- only:
- - master
- - /^maint.*/
-environment:
- GITTEST_INVASIVE_FS_STRUCTURE: 1
- GITTEST_INVASIVE_FS_SIZE: 1
-
- matrix:
- - GENERATOR: "Visual Studio 11"
- ARCH: 32
- - GENERATOR: "Visual Studio 11 Win64"
- ARCH: 64
- - GENERATOR: "MSYS Makefiles"
- ARCH: i686 # this is for 32-bit MinGW-w64
- - GENERATOR: "MSYS Makefiles"
- ARCH: 64
-cache:
-- i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z
-- x86_64-4.9.2-release-win32-seh-rt_v3-rev1.7z
-
-build_script:
-- ps: |
- mkdir build
- cd build
- if ($env:GENERATOR -ne "MSYS Makefiles") {
- cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON -D MSVC_CRTDBG=ON .. -G"$env:GENERATOR"
- cmake --build . --config Debug
- }
-- cmd: |
- if "%GENERATOR%"=="MSYS Makefiles" (C:\MinGW\msys\1.0\bin\sh --login /c/projects/libgit2/script/appveyor-mingw.sh)
-test_script:
-- ps: |
- $ErrorActionPreference="Stop"
- Start-FileDownload https://github.com/ethomson/poxyproxy/releases/download/v0.1.0/poxyproxy-0.1.0.jar -FileName poxyproxy.jar
- # Run this early so we know it's ready by the time we need it
- $proxyJob = Start-Job { java -jar $Env:APPVEYOR_BUILD_FOLDER\build\poxyproxy.jar -d --port 8080 --credentials foo:bar }
- ctest -V -R libgit2_clar
- $env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
- $env:GITTEST_REMOTE_USER="libgit2test"
- ctest -V -R libgit2_clar-cred_callback
- Receive-Job -Job $proxyJob
- $env:GITTEST_REMOTE_PROXY_URL = "http://foo:bar@localhost:8080"
- ctest -V -R libgit2_clar-proxy_credentials_in_url
- $env:GITTEST_REMOTE_PROXY_URL = "http://localhost:8080"
- $env:GITTEST_REMOTE_PROXY_USER = "foo"
- $env:GITTEST_REMOTE_PROXY_PASS = "bar"
- ctest -V -R libgit2_clar-proxy_credentials_request
+++ /dev/null
-# - Append compiler flag to CMAKE_C_FLAGS if compiler supports it
-# ADD_C_FLAG_IF_SUPPORTED(<flag>)
-# <flag> - the compiler flag to test
-# This internally calls the CHECK_C_COMPILER_FLAG macro.
-
-INCLUDE(CheckCCompilerFlag)
-
-MACRO(ADD_C_FLAG_IF_SUPPORTED _FLAG)
- STRING(TOUPPER ${_FLAG} UPCASE)
- STRING(REGEX REPLACE "^-" "" UPCASE_PRETTY ${UPCASE})
- CHECK_C_COMPILER_FLAG(${_FLAG} IS_${UPCASE_PRETTY}_SUPPORTED)
-
- IF(IS_${UPCASE_PRETTY}_SUPPORTED)
- SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_FLAG}")
- ENDIF()
-ENDMACRO()
+++ /dev/null
-IF (COREFOUNDATION_INCLUDE_DIR AND COREFOUNDATION_DIRS)
- SET(COREFOUNDATION_FOUND TRUE)
-ELSE ()
- FIND_PATH(COREFOUNDATION_INCLUDE_DIR NAMES CoreFoundation.h)
- FIND_LIBRARY(COREFOUNDATION_DIRS NAMES CoreFoundation)
- IF (COREFOUNDATION_INCLUDE_DIR AND COREFOUNDATION_DIRS)
- SET(COREFOUNDATION_FOUND TRUE)
- ENDIF ()
-ENDIF ()
+++ /dev/null
-# - Try to find GSSAPI
-# Once done this will define
-#
-# KRB5_CONFIG - Path to krb5-config
-# GSSAPI_ROOT_DIR - Set this variable to the root installation of GSSAPI
-#
-# Read-Only variables:
-# GSSAPI_FLAVOR_MIT - set to TURE if MIT Kerberos has been found
-# GSSAPI_FLAVOR_HEIMDAL - set to TRUE if Heimdal Keberos has been found
-# GSSAPI_FOUND - system has GSSAPI
-# GSSAPI_INCLUDE_DIR - the GSSAPI include directory
-# GSSAPI_LIBRARIES - Link these to use GSSAPI
-# GSSAPI_DEFINITIONS - Compiler switches required for using GSSAPI
-#
-#=============================================================================
-# Copyright (c) 2013 Andreas Schneider <asn@cryptomilk.org>
-#
-# Distributed under the OSI-approved BSD License (the "License");
-# see accompanying file Copyright.txt for details.
-#
-# This software is distributed WITHOUT ANY WARRANTY; without even the
-# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the License for more information.
-#=============================================================================
-#
-
-find_path(GSSAPI_ROOT_DIR
- NAMES
- include/gssapi.h
- include/gssapi/gssapi.h
- HINTS
- ${_GSSAPI_ROOT_HINTS}
- PATHS
- ${_GSSAPI_ROOT_PATHS}
-)
-mark_as_advanced(GSSAPI_ROOT_DIR)
-
-if (UNIX)
- find_program(KRB5_CONFIG
- NAMES
- krb5-config
- PATHS
- ${GSSAPI_ROOT_DIR}/bin
- /opt/local/bin)
- mark_as_advanced(KRB5_CONFIG)
-
- if (KRB5_CONFIG)
- # Check if we have MIT KRB5
- execute_process(
- COMMAND
- ${KRB5_CONFIG} --vendor
- RESULT_VARIABLE
- _GSSAPI_VENDOR_RESULT
- OUTPUT_VARIABLE
- _GSSAPI_VENDOR_STRING)
-
- if (_GSSAPI_VENDOR_STRING MATCHES ".*Massachusetts.*")
- set(GSSAPI_FLAVOR_MIT TRUE)
- else()
- execute_process(
- COMMAND
- ${KRB5_CONFIG} --libs gssapi
- RESULT_VARIABLE
- _GSSAPI_LIBS_RESULT
- OUTPUT_VARIABLE
- _GSSAPI_LIBS_STRING)
-
- if (_GSSAPI_LIBS_STRING MATCHES ".*roken.*")
- set(GSSAPI_FLAVOR_HEIMDAL TRUE)
- endif()
- endif()
-
- # Get the include dir
- execute_process(
- COMMAND
- ${KRB5_CONFIG} --cflags gssapi
- RESULT_VARIABLE
- _GSSAPI_INCLUDE_RESULT
- OUTPUT_VARIABLE
- _GSSAPI_INCLUDE_STRING)
- string(REGEX REPLACE "(\r?\n)+$" "" _GSSAPI_INCLUDE_STRING "${_GSSAPI_INCLUDE_STRING}")
- string(REGEX REPLACE " *-I" "" _GSSAPI_INCLUDEDIR "${_GSSAPI_INCLUDE_STRING}")
- endif()
-
- if (NOT GSSAPI_FLAVOR_MIT AND NOT GSSAPI_FLAVOR_HEIMDAL)
- # Check for HEIMDAL
- find_package(PkgConfig)
- if (PKG_CONFIG_FOUND)
- pkg_check_modules(_GSSAPI heimdal-gssapi)
- endif (PKG_CONFIG_FOUND)
-
- if (_GSSAPI_FOUND)
- set(GSSAPI_FLAVOR_HEIMDAL TRUE)
- else()
- find_path(_GSSAPI_ROKEN
- NAMES
- roken.h
- PATHS
- ${GSSAPI_ROOT_DIR}/include
- ${_GSSAPI_INCLUDEDIR})
- if (_GSSAPI_ROKEN)
- set(GSSAPI_FLAVOR_HEIMDAL TRUE)
- endif()
- endif ()
- endif()
-endif (UNIX)
-
-find_path(GSSAPI_INCLUDE_DIR
- NAMES
- gssapi.h
- gssapi/gssapi.h
- PATHS
- ${GSSAPI_ROOT_DIR}/include
- ${_GSSAPI_INCLUDEDIR}
-)
-
-if (GSSAPI_FLAVOR_MIT)
- find_library(GSSAPI_LIBRARY
- NAMES
- gssapi_krb5
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(KRB5_LIBRARY
- NAMES
- krb5
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(K5CRYPTO_LIBRARY
- NAMES
- k5crypto
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(COM_ERR_LIBRARY
- NAMES
- com_err
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- if (GSSAPI_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${GSSAPI_LIBRARY}
- )
- endif (GSSAPI_LIBRARY)
-
- if (KRB5_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${KRB5_LIBRARY}
- )
- endif (KRB5_LIBRARY)
-
- if (K5CRYPTO_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${K5CRYPTO_LIBRARY}
- )
- endif (K5CRYPTO_LIBRARY)
-
- if (COM_ERR_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${COM_ERR_LIBRARY}
- )
- endif (COM_ERR_LIBRARY)
-endif (GSSAPI_FLAVOR_MIT)
-
-if (GSSAPI_FLAVOR_HEIMDAL)
- find_library(GSSAPI_LIBRARY
- NAMES
- gssapi
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(KRB5_LIBRARY
- NAMES
- krb5
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(HCRYPTO_LIBRARY
- NAMES
- hcrypto
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(COM_ERR_LIBRARY
- NAMES
- com_err
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(HEIMNTLM_LIBRARY
- NAMES
- heimntlm
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(HX509_LIBRARY
- NAMES
- hx509
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(ASN1_LIBRARY
- NAMES
- asn1
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(WIND_LIBRARY
- NAMES
- wind
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- find_library(ROKEN_LIBRARY
- NAMES
- roken
- PATHS
- ${GSSAPI_ROOT_DIR}/lib
- ${_GSSAPI_LIBDIR}
- )
-
- if (GSSAPI_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${GSSAPI_LIBRARY}
- )
- endif (GSSAPI_LIBRARY)
-
- if (KRB5_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${KRB5_LIBRARY}
- )
- endif (KRB5_LIBRARY)
-
- if (HCRYPTO_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${HCRYPTO_LIBRARY}
- )
- endif (HCRYPTO_LIBRARY)
-
- if (COM_ERR_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${COM_ERR_LIBRARY}
- )
- endif (COM_ERR_LIBRARY)
-
- if (HEIMNTLM_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${HEIMNTLM_LIBRARY}
- )
- endif (HEIMNTLM_LIBRARY)
-
- if (HX509_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${HX509_LIBRARY}
- )
- endif (HX509_LIBRARY)
-
- if (ASN1_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${ASN1_LIBRARY}
- )
- endif (ASN1_LIBRARY)
-
- if (WIND_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${WIND_LIBRARY}
- )
- endif (WIND_LIBRARY)
-
- if (ROKEN_LIBRARY)
- set(GSSAPI_LIBRARIES
- ${GSSAPI_LIBRARIES}
- ${WIND_LIBRARY}
- )
- endif (ROKEN_LIBRARY)
-endif (GSSAPI_FLAVOR_HEIMDAL)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(GSSAPI DEFAULT_MSG GSSAPI_LIBRARIES GSSAPI_INCLUDE_DIR)
-
-if (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
- set(GSSAPI_FOUND TRUE)
-endif (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
-
-# show the GSSAPI_INCLUDE_DIRS and GSSAPI_LIBRARIES variables only in the advanced view
-mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES)
+++ /dev/null
-# - Try to find http-parser
-#
-# Defines the following variables:
-#
-# HTTP_PARSER_FOUND - system has http-parser
-# HTTP_PARSER_INCLUDE_DIR - the http-parser include directory
-# HTTP_PARSER_LIBRARIES - Link these to use http-parser
-# HTTP_PARSER_VERSION_MAJOR - major version
-# HTTP_PARSER_VERSION_MINOR - minor version
-# HTTP_PARSER_VERSION_STRING - the version of http-parser found
-
-# Find the header and library
-FIND_PATH(HTTP_PARSER_INCLUDE_DIR NAMES http_parser.h)
-FIND_LIBRARY(HTTP_PARSER_LIBRARY NAMES http_parser libhttp_parser)
-
-# Found the header, read version
-if (HTTP_PARSER_INCLUDE_DIR AND EXISTS "${HTTP_PARSER_INCLUDE_DIR}/http_parser.h")
- FILE(READ "${HTTP_PARSER_INCLUDE_DIR}/http_parser.h" HTTP_PARSER_H)
- IF (HTTP_PARSER_H)
- STRING(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_MAJOR[\t ]+([0-9]+).*" "\\1" HTTP_PARSER_VERSION_MAJOR "${HTTP_PARSER_H}")
- STRING(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_MINOR[\t ]+([0-9]+).*" "\\1" HTTP_PARSER_VERSION_MINOR "${HTTP_PARSER_H}")
- SET(HTTP_PARSER_VERSION_STRING "${HTTP_PARSER_VERSION_MAJOR}.${HTTP_PARSER_VERSION_MINOR}")
- ENDIF()
- UNSET(HTTP_PARSER_H)
-ENDIF()
-
-# Handle the QUIETLY and REQUIRED arguments and set HTTP_PARSER_FOUND
-# to TRUE if all listed variables are TRUE
-INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(HTTP_Parser REQUIRED_VARS HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY)
-
-# Hide advanced variables
-MARK_AS_ADVANCED(HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY)
-
-# Set standard variables
-IF (HTTP_PARSER_FOUND)
- SET(HTTP_PARSER_LIBRARIES ${HTTP_PARSER_LIBRARY})
- set(HTTP_PARSER_INCLUDE_DIRS ${HTTP_PARSER_INCLUDE_DIR})
-ENDIF()
+++ /dev/null
-# - Try to find Iconv
-# Once done this will define
-#
-# ICONV_FOUND - system has Iconv
-# ICONV_INCLUDE_DIR - the Iconv include directory
-# ICONV_LIBRARIES - Link these to use Iconv
-#
-
-IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
- # Already in cache, be silent
- SET(ICONV_FIND_QUIETLY TRUE)
-ENDIF()
-
-FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
-FIND_LIBRARY(iconv_lib NAMES iconv libiconv libiconv-2 c)
-
-IF(ICONV_INCLUDE_DIR AND iconv_lib)
- SET(ICONV_FOUND TRUE)
-ENDIF()
-
-IF(ICONV_FOUND)
- #Â split iconv into -L and -l linker options, so we can set them for pkg-config
- GET_FILENAME_COMPONENT(iconv_path ${iconv_lib} PATH)
- GET_FILENAME_COMPONENT(iconv_name ${iconv_lib} NAME_WE)
- STRING(REGEX REPLACE "^lib" "" iconv_name ${iconv_name})
- SET(ICONV_LIBRARIES "-L${iconv_path} -l${iconv_name}")
-
- IF(NOT ICONV_FIND_QUIETLY)
- MESSAGE(STATUS "Found Iconv: ${ICONV_LIBRARIES}")
- ENDIF(NOT ICONV_FIND_QUIETLY)
-ELSE()
- IF(Iconv_FIND_REQUIRED)
- MESSAGE(FATAL_ERROR "Could not find Iconv")
- ENDIF(Iconv_FIND_REQUIRED)
-ENDIF()
-
-MARK_AS_ADVANCED(
- ICONV_INCLUDE_DIR
- ICONV_LIBRARIES
-)
+++ /dev/null
-IF (SECURITY_INCLUDE_DIR AND SECURITY_DIRS)
- SET(SECURITY_FOUND TRUE)
-ELSE ()
- FIND_PATH(SECURITY_INCLUDE_DIR NAMES Security/Security.h)
- FIND_LIBRARY(SECURITY_DIRS NAMES Security)
- IF (SECURITY_INCLUDE_DIR AND SECURITY_DIRS)
- SET(SECURITY_FOUND TRUE)
- ENDIF ()
-ENDIF ()
+++ /dev/null
-Checkout Internals
-==================
-
-Checkout has to handle a lot of different cases. It examines the
-differences between the target tree, the baseline tree and the working
-directory, plus the contents of the index, and groups files into five
-categories:
-
-1. UNMODIFIED - Files that match in all places.
-2. SAFE - Files where the working directory and the baseline content
- match that can be safely updated to the target.
-3. DIRTY/MISSING - Files where the working directory differs from the
- baseline but there is no conflicting change with the target. One
- example is a file that doesn't exist in the working directory - no
- data would be lost as a result of writing this file. Which action
- will be taken with these files depends on the options you use.
-4. CONFLICTS - Files where changes in the working directory conflict
- with changes to be applied by the target. If conflicts are found,
- they prevent any other modifications from being made (although there
- are options to override that and force the update, of course).
-5. UNTRACKED/IGNORED - Files in the working directory that are untracked
- or ignored (i.e. only in the working directory, not the other places).
-
-Right now, this classification is done via 3 iterators (for the three
-trees), with a final lookup in the index. At some point, this may move to
-a 4 iterator version to incorporate the index better.
-
-The actual checkout is done in five phases (at least right now).
-
-1. The diff between the baseline and the target tree is used as a base
- list of possible updates to be applied.
-2. Iterate through the diff and the working directory, building a list of
- actions to be taken (and sending notifications about conflicts and
- dirty files).
-3. Remove any files / directories as needed (because alphabetical
- iteration means that an untracked directory will end up sorted *after*
- a blob that should be checked out with the same name).
-4. Update all blobs.
-5. Update all submodules (after 4 in case a new .gitmodules blob was
- checked out)
-
-Checkout could be driven either off a target-to-workdir diff or a
-baseline-to-target diff. There are pros and cons of each.
-
-Target-to-workdir means the diff includes every file that could be
-modified, which simplifies bookkeeping, but the code to constantly refer
-back to the baseline gets complicated.
-
-Baseline-to-target has simpler code because the diff defines the action to
-take, but needs special handling for untracked and ignored files, if they
-need to be removed.
-
-The current checkout implementation is based on a baseline-to-target diff.
-
-
-Picking Actions
-===============
-
-The most interesting aspect of this is phase 2, picking the actions that
-should be taken. There are a lot of corner cases, so it may be easier to
-start by looking at the rules for a simple 2-iterator diff:
-
-Key
----
-- B1,B2,B3 - blobs with different SHAs,
-- Bi - ignored blob (WD only)
-- T1,T2,T3 - trees with different SHAs,
-- Ti - ignored tree (WD only)
-- S1,S2 - submodules with different SHAs
-- Sd - dirty submodule (WD only)
-- x - nothing
-
-Diff with 2 non-workdir iterators
----------------------------------
-
-| | Old | New | |
-|----|-----|-----|------------------------------------------------------------|
-| 0 | x | x | nothing |
-| 1 | x | B1 | added blob |
-| 2 | x | T1 | added tree |
-| 3 | B1 | x | removed blob |
-| 4 | B1 | B1 | unmodified blob |
-| 5 | B1 | B2 | modified blob |
-| 6 | B1 | T1 | typechange blob -> tree |
-| 7 | T1 | x | removed tree |
-| 8 | T1 | B1 | typechange tree -> blob |
-| 9 | T1 | T1 | unmodified tree |
-| 10 | T1 | T2 | modified tree (implies modified/added/removed blob inside) |
-
-
-Now, let's make the "New" iterator into a working directory iterator, so
-we replace "added" items with either untracked or ignored, like this:
-
-Diff with non-work & workdir iterators
---------------------------------------
-
-| | Old | New | |
-|----|-----|-----|------------------------------------------------------------|
-| 0 | x | x | nothing |
-| 1 | x | B1 | untracked blob |
-| 2 | x | Bi | ignored file |
-| 3 | x | T1 | untracked tree |
-| 4 | x | Ti | ignored tree |
-| 5 | B1 | x | removed blob |
-| 6 | B1 | B1 | unmodified blob |
-| 7 | B1 | B2 | modified blob |
-| 8 | B1 | T1 | typechange blob -> tree |
-| 9 | B1 | Ti | removed blob AND ignored tree as separate items |
-| 10 | T1 | x | removed tree |
-| 11 | T1 | B1 | typechange tree -> blob |
-| 12 | T1 | Bi | removed tree AND ignored blob as separate items |
-| 13 | T1 | T1 | unmodified tree |
-| 14 | T1 | T2 | modified tree (implies modified/added/removed blob inside) |
-
-Note: if there is a corresponding entry in the old tree, then a working
-directory item won't be ignored (i.e. no Bi or Ti for tracked items).
-
-
-Now, expand this to three iterators: a baseline tree, a target tree, and
-an actual working directory tree:
-
-Checkout From 3 Iterators (2 not workdir, 1 workdir)
-----------------------------------------------------
-
-(base == old HEAD; target == what to checkout; actual == working dir)
-
-| |base | target | actual/workdir | |
-|-----|-----|------- |----------------|--------------------------------------------------------------------|
-| 0 | x | x | x | nothing |
-| 1 | x | x | B1/Bi/T1/Ti | untracked/ignored blob/tree (SAFE) |
-| 2+ | x | B1 | x | add blob (SAFE) |
-| 3 | x | B1 | B1 | independently added blob (FORCEABLE-2) |
-| 4* | x | B1 | B2/Bi/T1/Ti | add blob with content conflict (FORCEABLE-2) |
-| 5+ | x | T1 | x | add tree (SAFE) |
-| 6* | x | T1 | B1/Bi | add tree with blob conflict (FORCEABLE-2) |
-| 7 | x | T1 | T1/i | independently added tree (SAFE+MISSING) |
-| 8 | B1 | x | x | independently deleted blob (SAFE+MISSING) |
-| 9- | B1 | x | B1 | delete blob (SAFE) |
-| 10- | B1 | x | B2 | delete of modified blob (FORCEABLE-1) |
-| 11 | B1 | x | T1/Ti | independently deleted blob AND untrack/ign tree (SAFE+MISSING !!!) |
-| 12 | B1 | B1 | x | locally deleted blob (DIRTY || SAFE+CREATE) |
-| 13+ | B1 | B2 | x | update to deleted blob (SAFE+MISSING) |
-| 14 | B1 | B1 | B1 | unmodified file (SAFE) |
-| 15 | B1 | B1 | B2 | locally modified file (DIRTY) |
-| 16+ | B1 | B2 | B1 | update unmodified blob (SAFE) |
-| 17 | B1 | B2 | B2 | independently updated blob (FORCEABLE-1) |
-| 18+ | B1 | B2 | B3 | update to modified blob (FORCEABLE-1) |
-| 19 | B1 | B1 | T1/Ti | locally deleted blob AND untrack/ign tree (DIRTY) |
-| 20* | B1 | B2 | T1/Ti | update to deleted blob AND untrack/ign tree (F-1) |
-| 21+ | B1 | T1 | x | add tree with locally deleted blob (SAFE+MISSING) |
-| 22* | B1 | T1 | B1 | add tree AND deleted blob (SAFE) |
-| 23* | B1 | T1 | B2 | add tree with delete of modified blob (F-1) |
-| 24 | B1 | T1 | T1 | add tree with deleted blob (F-1) |
-| 25 | T1 | x | x | independently deleted tree (SAFE+MISSING) |
-| 26 | T1 | x | B1/Bi | independently deleted tree AND untrack/ign blob (F-1) |
-| 27- | T1 | x | T1 | deleted tree (MAYBE SAFE) |
-| 28+ | T1 | B1 | x | deleted tree AND added blob (SAFE+MISSING) |
-| 29 | T1 | B1 | B1 | independently typechanged tree -> blob (F-1) |
-| 30+ | T1 | B1 | B2 | typechange tree->blob with conflicting blob (F-1) |
-| 31* | T1 | B1 | T1/T2 | typechange tree->blob (MAYBE SAFE) |
-| 32+ | T1 | T1 | x | restore locally deleted tree (SAFE+MISSING) |
-| 33 | T1 | T1 | B1/Bi | locally typechange tree->untrack/ign blob (DIRTY) |
-| 34 | T1 | T1 | T1/T2 | unmodified tree (MAYBE SAFE) |
-| 35+ | T1 | T2 | x | update locally deleted tree (SAFE+MISSING) |
-| 36* | T1 | T2 | B1/Bi | update to tree with typechanged tree->blob conflict (F-1) |
-| 37 | T1 | T2 | T1/T2/T3 | update to existing tree (MAYBE SAFE) |
-| 38+ | x | S1 | x | add submodule (SAFE) |
-| 39 | x | S1 | S1/Sd | independently added submodule (SUBMODULE) |
-| 40* | x | S1 | B1 | add submodule with blob confilct (FORCEABLE) |
-| 41* | x | S1 | T1 | add submodule with tree conflict (FORCEABLE) |
-| 42 | S1 | x | S1/Sd | deleted submodule (SUBMODULE) |
-| 43 | S1 | x | x | independently deleted submodule (SUBMODULE) |
-| 44 | S1 | x | B1 | independently deleted submodule with added blob (SAFE+MISSING) |
-| 45 | S1 | x | T1 | independently deleted submodule with added tree (SAFE+MISSING) |
-| 46 | S1 | S1 | x | locally deleted submodule (SUBMODULE) |
-| 47+ | S1 | S2 | x | update locally deleted submodule (SAFE) |
-| 48 | S1 | S1 | S2 | locally updated submodule commit (SUBMODULE) |
-| 49 | S1 | S2 | S1 | updated submodule commit (SUBMODULE) |
-| 50+ | S1 | B1 | x | add blob with locally deleted submodule (SAFE+MISSING) |
-| 51* | S1 | B1 | S1 | typechange submodule->blob (SAFE) |
-| 52* | S1 | B1 | Sd | typechange dirty submodule->blob (SAFE!?!?) |
-| 53+ | S1 | T1 | x | add tree with locally deleted submodule (SAFE+MISSING) |
-| 54* | S1 | T1 | S1/Sd | typechange submodule->tree (MAYBE SAFE) |
-| 55+ | B1 | S1 | x | add submodule with locally deleted blob (SAFE+MISSING) |
-| 56* | B1 | S1 | B1 | typechange blob->submodule (SAFE) |
-| 57+ | T1 | S1 | x | add submodule with locally deleted tree (SAFE+MISSING) |
-| 58* | T1 | S1 | T1 | typechange tree->submodule (SAFE) |
-
-
-The number is followed by ' ' if no change is needed or '+' if the case
-needs to write to disk or '-' if something must be deleted and '*' if
-there should be a delete followed by an write.
-
-There are four tiers of safe cases:
-
-* SAFE == completely safe to update
-* SAFE+MISSING == safe except the workdir is missing the expect content
-* MAYBE SAFE == safe if workdir tree matches (or is missing) baseline
- content, which is unknown at this point
-* FORCEABLE == conflict unless FORCE is given
-* DIRTY == no conflict but change is not applied unless FORCE
-* SUBMODULE == no conflict and no change is applied unless a deleted
- submodule dir is empty
-
-Some slightly unusual circumstances:
-
-* 8 - parent dir is only deleted when file is, so parent will be left if
- empty even though it would be deleted if the file were present
-* 11 - core git does not consider this a conflict but attempts to delete T1
- and gives "unable to unlink file" error yet does not skip the rest
- of the operation
-* 12 - without FORCE file is left deleted (i.e. not restored) so new wd is
- dirty (and warning message "D file" is printed), with FORCE, file is
- restored.
-* 24 - This should be considered MAYBE SAFE since effectively it is 7 and 8
- combined, but core git considers this a conflict unless forced.
-* 26 - This combines two cases (1 & 25) (and also implied 8 for tree content)
- which are ok on their own, but core git treat this as a conflict.
- If not forced, this is a conflict. If forced, this actually doesn't
- have to write anything and leaves the new blob as an untracked file.
-* 32 - This is the only case where the baseline and target values match
- and yet we will still write to the working directory. In all other
- cases, if baseline == target, we don't touch the workdir (it is
- either already right or is "dirty"). However, since this case also
- implies that a ?/B1/x case will exist as well, it can be skipped.
-* 41 - It's not clear how core git distinguishes this case from 39 (mode?).
-* 52 - Core git makes destructive changes without any warning when the
- submodule is dirty and the type changes to a blob.
-
-Cases 3, 17, 24, 26, and 29 are all considered conflicts even though
-none of them will require making any updates to the working directory.
+++ /dev/null
-Diff is broken into four phases:
-
-1. Building a list of things that have changed. These changes are called
- deltas (git_diff_delta objects) and are grouped into a git_diff_list.
-2. Applying file similarity measurement for rename and copy detection (and
- to potentially split files that have changed radically). This step is
- optional.
-3. Computing the textual diff for each delta. Not all deltas have a
- meaningful textual diff. For those that do, the textual diff can
- either be generated on the fly and passed to output callbacks or can be
- turned into a git_diff_patch object.
-4. Formatting the diff and/or patch into standard text formats (such as
- patches, raw lists, etc).
-
-In the source code, step 1 is implemented in `src/diff.c`, step 2 in
-`src/diff_tform.c`, step 3 in `src/diff_patch.c`, and step 4 in
-`src/diff_print.c`. Additionally, when it comes to accessing file
-content, everything goes through diff drivers that are implemented in
-`src/diff_driver.c`.
-
-External Objects
-----------------
-
-* `git_diff_options` represents user choices about how a diff should be
- performed and is passed to most diff generating functions.
-* `git_diff_file` represents an item on one side of a possible delta
-* `git_diff_delta` represents a pair of items that have changed in some
- way - it contains two `git_diff_file` plus a status and other stuff.
-* `git_diff_list` is a list of deltas along with information about how
- those particular deltas were found.
-* `git_diff_patch` represents the actual diff between a pair of items. In
- some cases, a delta may not have a corresponding patch, if the objects
- are binary, for example. The content of a patch will be a set of hunks
- and lines.
-* A `hunk` is range of lines described by a `git_diff_range` (i.e. "lines
- 10-20 in the old file became lines 12-23 in the new"). It will have a
- header that compactly represents that information, and it will have a
- number of lines of context surrounding added and deleted lines.
-* A `line` is simple a line of data along with a `git_diff_line_t` value
- that tells how the data should be interpreted (e.g. context or added).
-
-Internal Objects
-----------------
-
-* `git_diff_file_content` is an internal structure that represents the
- data on one side of an item to be diffed; it is an augmented
- `git_diff_file` with more flags and the actual file data.
-
- * it is created from a repository plus a) a git_diff_file, b) a git_blob,
- or c) raw data and size
- * there are three main operations on git_diff_file_content:
-
- * _initialization_ sets up the data structure and does what it can up to,
- but not including loading and looking at the actual data
- * _loading_ loads the data, preprocesses it (i.e. applies filters) and
- potentially analyzes it (to decide if binary)
- * _free_ releases loaded data and frees any allocated memory
-
-* The internal structure of a `git_diff_patch` stores the actual diff
- between a pair of `git_diff_file_content` items
-
- * it may be "unset" if the items are not diffable
- * "empty" if the items are the same
- * otherwise it will consist of a set of hunks each of which covers some
- number of lines of context, additions and deletions
- * a patch is created from two git_diff_file_content items
- * a patch is fully instantiated in three phases:
-
- * initial creation and initialization
- * loading of data and preliminary data examination
- * diffing of data and optional storage of diffs
- * (TBD) if a patch is asked to store the diffs and the size of the diff
- is significantly smaller than the raw data of the two sides, then the
- patch may be flattened using a pool of string data
-
-* `git_diff_output` is an internal structure that represents an output
- target for a `git_diff_patch`
- * It consists of file, hunk, and line callbacks, plus a payload
- * There is a standard flattened output that can be used for plain text output
- * Typically we use a `git_xdiff_output` which drives the callbacks via the
- xdiff code taken from core Git.
-
-* `git_diff_driver` is an internal structure that encapsulates the logic
- for a given type of file
- * a driver is looked up based on the name and mode of a file.
- * the driver can then be used to:
- * determine if a file is binary (by attributes, by git_diff_options
- settings, or by examining the content)
- * give you a function pointer that is used to evaluate function context
- for hunk headers
- * At some point, the logic for getting a filtered version of file content
- or calculating the OID of a file may be moved into the driver.
+++ /dev/null
-Error reporting in libgit2
-==========================
-
-Libgit2 tries to follow the POSIX style: functions return an `int` value
-with 0 (zero) indicating success and negative values indicating an error.
-There are specific negative error codes for each "expected failure"
-(e.g. `GIT_ENOTFOUND` for files that take a path which might be missing)
-and a generic error code (-1) for all critical or non-specific failures
-(e.g. running out of memory or system corruption).
-
-When a negative value is returned, an error message is also set. The
-message can be accessed via the `giterr_last` function which will return a
-pointer to a `git_error` structure containing the error message text and
-the class of error (i.e. what part of the library generated the error).
-
-For instance: An object lookup by SHA prefix (`git_object_lookup_prefix`)
-has two expected failure cases: the SHA is not found at all which returns
-`GIT_ENOTFOUND` or the SHA prefix is ambiguous (i.e. two or more objects
-share the prefix) which returns `GIT_EAMBIGUOUS`. There are any number of
-critical failures (such as a packfile being corrupted, a loose object
-having the wrong access permissions, etc.) all of which will return -1.
-When the object lookup is successful, it will return 0.
-
-If libgit2 was compiled with threads enabled (`-DTHREADSAFE=ON` when using
-CMake), then the error message will be kept in thread-local storage, so it
-will not be modified by other threads. If threads are not enabled, then
-the error message is in global data.
-
-All of the error return codes, the `git_error` type, the error access
-functions, and the error classes are defined in `include/git2/errors.h`.
-See the documentation there for details on the APIs for accessing,
-clearing, and even setting error codes.
-
-When writing libgit2 code, please be smart and conservative when returning
-error codes. Functions usually have a maximum of two or three "expected
-errors" and in most cases only one. If you feel there are more possible
-expected error scenarios, then the API you are writing may be at too high
-a level for core libgit2.
-
-Example usage
--------------
-
-When using libgit2, you will typically capture the return value from
-functions using an `int` variable and check to see if it is negative.
-When that happens, you can, if you wish, look at the specific value or
-look at the error message that was generated.
-
-~~~c
-{
- git_repository *repo;
- int error = git_repository_open(&repo, "path/to/repo");
-
- if (error < 0) {
- fprintf(stderr, "Could not open repository: %s\n", giterr_last()->message);
- exit(1);
- }
-
- ... use `repo` here ...
-
- git_repository_free(repo); /* void function - no error return code */
-}
-~~~
-
-Some of the error return values do have meaning. Optionally, you can look
-at the specific error values to decide what to do.
-
-~~~c
-{
- git_repository *repo;
- const char *path = "path/to/repo";
- int error = git_repository_open(&repo, path);
-
- if (error < 0) {
- if (error == GIT_ENOTFOUND)
- fprintf(stderr, "Could not find repository at path '%s'\n", path);
- else
- fprintf(stderr, "Unable to open repository: %s\n",
- giterr_last()->message);
- exit(1);
- }
-
- ... happy ...
-}
-~~~
-
-Some of the higher-level language bindings may use a range of information
-from libgit2 to convert error return codes into exceptions, including the
-specific error return codes and even the class of error and the error
-message returned by `giterr_last`, but the full range of that logic is
-beyond the scope of this document.
-
-Example internal implementation
--------------------------------
-
-Internally, libgit2 detects error scenarios, records error messages, and
-returns error values. Errors from low-level functions are generally
-passed upwards (unless the higher level can either handle the error or
-wants to translate the error into something more meaningful).
-
-~~~c
-int git_repository_open(git_repository **repository, const char *path)
-{
- /* perform some logic to open the repository */
- if (p_exists(path) < 0) {
- giterr_set(GITERR_REPOSITORY, "The path '%s' doesn't exist", path);
- return GIT_ENOTFOUND;
- }
-
- ...
-}
-~~~
-
-The public error API
---------------------
-
-- `const git_error *giterr_last(void)`: The main function used to look up
- the last error. This may return NULL if no error has occurred.
- Otherwise this should return a `git_error` object indicating the class
- of error and the error message that was generated by the library.
-
- The last error is stored in thread-local storage when libgit2 is
- compiled with thread support, so you do not have to worry about another
- thread overwriting the value. When thread support is off, the last
- error is a global value.
-
- _Note_ There are some known bugs in the library where this may return
- NULL even when an error code was generated. Please report these as
- bugs, but in the meantime, please code defensively and check for NULL
- when calling this function.
-
-- `void giterr_clear(void)`: This function clears the last error. The
- library will call this when an error is generated by low level function
- and the higher level function handles the error.
-
- _Note_ There are some known bugs in the library where a low level
- function's error message is not cleared by higher level code that
- handles the error and returns zero. Please report these as bugs, but in
- the meantime, a zero return value from a libgit2 API does not guarantee
- that `giterr_last()` will return NULL.
-
-- `void giterr_set_str(int error_class, const char *message)`: This
- function can be used when writing a custom backend module to set the
- libgit2 error message. See the documentation on this function for its
- use. Normal usage of libgit2 will probably never need to call this API.
-
-- `void giterr_set_oom(void)`: This is a standard function for reporting
- an out-of-memory error. It is written in a manner that it doesn't have
- to allocate any extra memory in order to record the error, so this is
- the best way to report that scenario.
-
-Deviations from the standard
-----------------------------
-
-There are some public functions that do not return `int` values. There
-are two primary cases:
-
-* `void` return values: If a function has a `void` return, then it will
- never fail. This primary will be used for object destructors.
-
-* `git_xyz *` return values: These are simple accessor functions where the
- only meaningful error would typically be looking something up by index
- and having the index be out of bounds. In those cases, the function
- will typically return NULL.
-
-* Boolean return values: There are some cases where a function cannot fail
- and wants to return a boolean value. In those cases, we try to return 1
- for true and 0 for false. These cases are rare and the return value for
- the function should probably be an `unsigned int` to denote these cases.
- If you find an exception, please open an issue and let's fix it.
-
-There are a few other exceptions to these rules here and there in the
-library, but those are extremely rare and should probably be converted
-over to other to more standard patterns for usage. Feel free to open
-issues pointing these out.
-
-There are some known bugs in the library where some functions may return a
-negative value but not set an error message and some other functions may
-return zero (no error) and yet leave an error message set. Please report
-these cases as issues and they will be fixed. In the meanwhile, please
-code defensively, checking that the return value of `giterr_last` is not
-NULL before using it, and not relying on `giterr_last` to return NULL when
-a function returns 0 for success.
-
-The internal error API
-----------------------
-
-- `void giterr_set(int error_class, const char *fmt, ...)`: This is the
- main internal function for setting an error. It works like `printf` to
- format the error message. See the notes of `giterr_set_str` for a
- general description of how error messages are stored (and also about
- special handling for `error_class` of `GITERR_OS`).
-
-Writing error messages
-----------------------
-
-Here are some guidelines when writing error messages:
-
-- Use proper English, and an impersonal or past tenses: *The given path
- does not exist*, *Failed to lookup object in ODB*
-
-- Use short, direct and objective messages. **One line, max**. libgit2 is
- a low level library: think that all the messages reported will be thrown
- as Ruby or Python exceptions. Think how long are common exception
- messages in those languages.
-
-- **Do not add redundant information to the error message**, specially
- information that can be inferred from the context.
-
- E.g. in `git_repository_open`, do not report a message like "Failed to
- open repository: path not found". Somebody is calling that
- function. If it fails, they already know that the repository failed to
- open!
-
-General guidelines for error reporting
---------------------------------------
-
-- Libgit2 does not handle programming errors with these
- functions. Programming errors are `assert`ed, and when their source is
- internal, fixed as soon as possible. This is C, people.
-
- Example of programming errors that would **not** be handled: passing
- NULL to a function that expects a valid pointer; passing a `git_tree`
- to a function that expects a `git_commit`. All these cases need to be
- identified with `assert` and fixed asap.
-
- Example of a runtime error: failing to parse a `git_tree` because it
- contains invalid data. Failing to open a file because it doesn't exist
- on disk. These errors are handled, a meaningful error message is set,
- and an error code is returned.
-
-- In general, *do not* try to overwrite errors internally and *do*
- propagate error codes from lower level functions to the higher level.
- There are some cases where propagating an error code will be more
- confusing rather than less, so there are some exceptions to this rule,
- but the default behavior should be to simply clean up and pass the error
- on up to the caller.
-
- **WRONG**
-
- ~~~c
- int git_commit_parent(...)
- {
- ...
-
- if (git_commit_lookup(parent, repo, parent_id) < 0) {
- giterr_set(GITERR_COMMIT, "Overwrite lookup error message");
- return -1; /* mask error code */
- }
-
- ...
- }
- ~~~
-
- **RIGHT**
-
- ~~~c
- int git_commit_parent(...)
- {
- ...
-
- error = git_commit_lookup(parent, repo, parent_id);
- if (error < 0) {
- /* cleanup intermediate objects if necessary */
- /* leave error message and propagate error code */
- return error;
- }
-
- ...
- }
- ~~~
+++ /dev/null
-Anc / Our / Thr represent the ancestor / ours / theirs side of a merge
-from branch "branch" into HEAD. Workdir represents the expected files in
-the working directory. Index represents the expected files in the index,
-with stage markers.
-
- Anc Our Thr Workdir Index
-1 D D
- D/F D/F D/F [0]
-
-2 D D+ D~HEAD (mod/del) D/F [0]
- D/F D/F D [1]
- D [2]
-
-3 D D D/F D/F [0]
- D/F
-
-4 D D+ D~branch (mod/del) D/F [0]
- D/F D/F D [1]
- D [3]
-
-5 D D/F (add/add) D/F [2]
- D/F D/F [3]
- D/F
-
-6 D/F D/F D D [0]
- D
-
-7 D/F D/F+ D/F (mod/del) D/F [1]
- D D~branch (fil/dir) D/F [2]
- D [3]
-
-8 D/F D/F D D [0]
- D
-
-9 D/F D/F+ D/F (mod/del) D/F [1]
- D D~HEAD (fil/dir) D [2]
- D/F [3]
-
-10 D/F D/F (fil/dir) D/F [0]
- D D~HEAD D [2]
- D
+++ /dev/null
-# This document lists the authors that have given voice to
-# their decision regarding relicensing the GPL'd code from
-# git.git to the GPL + gcc-exception license used by libgit2.
-#
-# Note that the permission is given for libgit2 use only. For
-# other uses, you must ask each of the contributors yourself.
-#
-# To show the owners of a file in git.git, one can run the
-# following command:
-#
-# git blame -C -C -M -- file | \
-# sed -e 's/[^(]*(\([^0-9]*\).*/\1/' -e 's/[\t ]*$//' | \
-# sort | uniq -c | sort -nr
-#
-# If everyone on the list that produces are on the list in
-# the recently added file "git.git-authors", it *should* be
-# safe to include that code in libgit2, but make sure to
-# read the file to ensure the code doesn't originate from
-# somewhere else.
-#
-# The format of this list is
-# "ok/no/ask/???"<tab>"Author"<space>"<email>"
-#
-# "ok" means the author consents to relicensing all their
-# contributed code (possibly with some exceptions)
-# "no" means the author does not consent
-# "ask" means that the contributor wants to give/withhold
-# his/her consent on a patch-by-patch basis.
-# "???" means the person is a prominent contributor who has
-# not yet made his/her standpoint clear.
-#
-# Please try to keep the list alphabetically ordered. It will
-# help in case we get all 600-ish git.git authors on it.
-#
-# (Paul Kocher is the author of the mozilla-sha1 implementation
-# but has otherwise not contributed to git.)
-#
-ok Adam Simpkins <adam@adamsimpkins.net> (http transport)
-ok Adrian Johnson <ajohnson@redneon.com>
-ok Alexey Shumkin <alex.crezoff@gmail.com>
-ok Andreas Ericsson <ae@op5.se>
-ok Antoine Pelisse <apelisse@gmail.com>
-ok Boyd Lynn Gerber <gerberb@zenez.com>
-ok Brandon Casey <drafnel@gmail.com>
-ok Brian Downing <bdowning@lavos.net>
-ok Brian Gernhardt <benji@silverinsanity.com>
-ok Christian Couder <chriscool@tuxfamily.org>
-ok Daniel Barkalow <barkalow@iabervon.org>
-ok Florian Forster <octo@verplant.org>
-ok Gustaf Hendeby <hendeby@isy.liu.se>
-ok Holger Weiss <holger@zedat.fu-berlin.de>
-ok Jeff King <peff@peff.net>
-ok Johannes Schindelin <Johannes.Schindelin@gmx.de>
-ok Johannes Sixt <j6t@kdbg.org>
-ask Jonathan Nieder <jrnieder@gmail.com>
-ok Junio C Hamano <gitster@pobox.com>
-ok Kristian Høgsberg <krh@redhat.com>
-ok Linus Torvalds <torvalds@linux-foundation.org>
-ok Lukas Sandström <lukass@etek.chalmers.se>
-ok Matthieu Moy <Matthieu.Moy@imag.fr>
-ok Michael Haggerty <mhagger@alum.mit.edu>
-ok Nicolas Pitre <nico@fluxnic.net> <nico@cam.org>
-ok Paolo Bonzini <bonzini@gnu.org>
-ok Paul Kocher <paul@cryptography.com>
-ok Peter Hagervall <hager@cs.umu.se>
-ok Petr Onderka <gsvick@gmail.com>
-ok Pierre Habouzit <madcoder@debian.org>
-ok Pieter de Bie <pdebie@ai.rug.nl>
-ok René Scharfe <rene.scharfe@lsrfire.ath.cx>
-ok Sebastian Schuberth <sschuberth@gmail.com>
-ok Shawn O. Pearce <spearce@spearce.org>
-ok Steffen Prohaska <prohaska@zib.de>
-ok Sven Verdoolaege <skimo@kotnet.org>
-ask Thomas Rast <tr@thomasrast.ch> (ok before 6-Oct-2013)
-ok Torsten Bögershausen <tboegi@web.de>
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_git_git_h__
-#define INCLUDE_git_git_h__
-
-#include "git2/annotated_commit.h"
-#include "git2/attr.h"
-#include "git2/blob.h"
-#include "git2/blame.h"
-#include "git2/branch.h"
-#include "git2/buffer.h"
-#include "git2/checkout.h"
-#include "git2/cherrypick.h"
-#include "git2/clone.h"
-#include "git2/commit.h"
-#include "git2/common.h"
-#include "git2/config.h"
-#include "git2/describe.h"
-#include "git2/diff.h"
-#include "git2/errors.h"
-#include "git2/filter.h"
-#include "git2/global.h"
-#include "git2/graph.h"
-#include "git2/ignore.h"
-#include "git2/index.h"
-#include "git2/indexer.h"
-#include "git2/merge.h"
-#include "git2/message.h"
-#include "git2/net.h"
-#include "git2/notes.h"
-#include "git2/object.h"
-#include "git2/odb.h"
-#include "git2/odb_backend.h"
-#include "git2/oid.h"
-#include "git2/pack.h"
-#include "git2/patch.h"
-#include "git2/pathspec.h"
-#include "git2/proxy.h"
-#include "git2/rebase.h"
-#include "git2/refdb.h"
-#include "git2/reflog.h"
-#include "git2/refs.h"
-#include "git2/refspec.h"
-#include "git2/remote.h"
-#include "git2/repository.h"
-#include "git2/reset.h"
-#include "git2/revert.h"
-#include "git2/revparse.h"
-#include "git2/revwalk.h"
-#include "git2/signature.h"
-#include "git2/stash.h"
-#include "git2/status.h"
-#include "git2/submodule.h"
-#include "git2/tag.h"
-#include "git2/transport.h"
-#include "git2/transaction.h"
-#include "git2/tree.h"
-#include "git2/types.h"
-#include "git2/version.h"
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_annotated_commit_h__
-#define INCLUDE_git_annotated_commit_h__
-
-#include "common.h"
-#include "repository.h"
-#include "types.h"
-
-/**
- * @file git2/annotated_commit.h
- * @brief Git annotated commit routines
- * @defgroup git_annotated_commit Git annotated commit routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Creates a `git_annotated_commit` from the given reference.
- * The resulting git_annotated_commit must be freed with
- * `git_annotated_commit_free`.
- *
- * @param out pointer to store the git_annotated_commit result in
- * @param repo repository that contains the given reference
- * @param ref reference to use to lookup the git_annotated_commit
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_annotated_commit_from_ref(
- git_annotated_commit **out,
- git_repository *repo,
- const git_reference *ref);
-
-/**
- * Creates a `git_annotated_commit` from the given fetch head data.
- * The resulting git_annotated_commit must be freed with
- * `git_annotated_commit_free`.
- *
- * @param out pointer to store the git_annotated_commit result in
- * @param repo repository that contains the given commit
- * @param branch_name name of the (remote) branch
- * @param remote_url url of the remote
- * @param id the commit object id of the remote branch
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_annotated_commit_from_fetchhead(
- git_annotated_commit **out,
- git_repository *repo,
- const char *branch_name,
- const char *remote_url,
- const git_oid *id);
-
-/**
- * Creates a `git_annotated_commit` from the given commit id.
- * The resulting git_annotated_commit must be freed with
- * `git_annotated_commit_free`.
- *
- * An annotated commit contains information about how it was
- * looked up, which may be useful for functions like merge or
- * rebase to provide context to the operation. For example,
- * conflict files will include the name of the source or target
- * branches being merged. It is therefore preferable to use the
- * most specific function (eg `git_annotated_commit_from_ref`)
- * instead of this one when that data is known.
- *
- * @param out pointer to store the git_annotated_commit result in
- * @param repo repository that contains the given commit
- * @param id the commit object id to lookup
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_annotated_commit_lookup(
- git_annotated_commit **out,
- git_repository *repo,
- const git_oid *id);
-
-/**
- * Creates a `git_annotated_comit` from a revision string.
- *
- * See `man gitrevisions`, or
- * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
- * information on the syntax accepted.
- *
- * @param out pointer to store the git_annotated_commit result in
- * @param repo repository that contains the given commit
- * @param revspec the extended sha syntax string to use to lookup the commit
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_annotated_commit_from_revspec(
- git_annotated_commit **out,
- git_repository *repo,
- const char *revspec);
-
-/**
- * Gets the commit ID that the given `git_annotated_commit` refers to.
- *
- * @param commit the given annotated commit
- * @return commit id
- */
-GIT_EXTERN(const git_oid *) git_annotated_commit_id(
- const git_annotated_commit *commit);
-
-/**
- * Frees a `git_annotated_commit`.
- *
- * @param commit annotated commit to free
- */
-GIT_EXTERN(void) git_annotated_commit_free(
- git_annotated_commit *commit);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_attr_h__
-#define INCLUDE_git_attr_h__
-
-#include "common.h"
-#include "types.h"
-
-/**
- * @file git2/attr.h
- * @brief Git attribute management routines
- * @defgroup git_attr Git attribute management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * GIT_ATTR_TRUE checks if an attribute is set on. In core git
- * parlance, this the value for "Set" attributes.
- *
- * For example, if the attribute file contains:
- *
- * *.c foo
- *
- * Then for file `xyz.c` looking up attribute "foo" gives a value for
- * which `GIT_ATTR_TRUE(value)` is true.
- */
-#define GIT_ATTR_TRUE(attr) (git_attr_value(attr) == GIT_ATTR_TRUE_T)
-
-/**
- * GIT_ATTR_FALSE checks if an attribute is set off. In core git
- * parlance, this is the value for attributes that are "Unset" (not to
- * be confused with values that a "Unspecified").
- *
- * For example, if the attribute file contains:
- *
- * *.h -foo
- *
- * Then for file `zyx.h` looking up attribute "foo" gives a value for
- * which `GIT_ATTR_FALSE(value)` is true.
- */
-#define GIT_ATTR_FALSE(attr) (git_attr_value(attr) == GIT_ATTR_FALSE_T)
-
-/**
- * GIT_ATTR_UNSPECIFIED checks if an attribute is unspecified. This
- * may be due to the attribute not being mentioned at all or because
- * the attribute was explicitly set unspecified via the `!` operator.
- *
- * For example, if the attribute file contains:
- *
- * *.c foo
- * *.h -foo
- * onefile.c !foo
- *
- * Then for `onefile.c` looking up attribute "foo" yields a value with
- * `GIT_ATTR_UNSPECIFIED(value)` of true. Also, looking up "foo" on
- * file `onefile.rb` or looking up "bar" on any file will all give
- * `GIT_ATTR_UNSPECIFIED(value)` of true.
- */
-#define GIT_ATTR_UNSPECIFIED(attr) (git_attr_value(attr) == GIT_ATTR_UNSPECIFIED_T)
-
-/**
- * GIT_ATTR_HAS_VALUE checks if an attribute is set to a value (as
- * opposed to TRUE, FALSE or UNSPECIFIED). This would be the case if
- * for a file with something like:
- *
- * *.txt eol=lf
- *
- * Given this, looking up "eol" for `onefile.txt` will give back the
- * string "lf" and `GIT_ATTR_SET_TO_VALUE(attr)` will return true.
- */
-#define GIT_ATTR_HAS_VALUE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_T)
-
-/**
- * Possible states for an attribute
- */
-typedef enum {
- GIT_ATTR_UNSPECIFIED_T = 0, /**< The attribute has been left unspecified */
- GIT_ATTR_TRUE_T, /**< The attribute has been set */
- GIT_ATTR_FALSE_T, /**< The attribute has been unset */
- GIT_ATTR_VALUE_T, /**< This attribute has a value */
-} git_attr_t;
-
-/**
- * Return the value type for a given attribute.
- *
- * This can be either `TRUE`, `FALSE`, `UNSPECIFIED` (if the attribute
- * was not set at all), or `VALUE`, if the attribute was set to an
- * actual string.
- *
- * If the attribute has a `VALUE` string, it can be accessed normally
- * as a NULL-terminated C string.
- *
- * @param attr The attribute
- * @return the value type for the attribute
- */
-GIT_EXTERN(git_attr_t) git_attr_value(const char *attr);
-
-/**
- * Check attribute flags: Reading values from index and working directory.
- *
- * When checking attributes, it is possible to check attribute files
- * in both the working directory (if there is one) and the index (if
- * there is one). You can explicitly choose where to check and in
- * which order using the following flags.
- *
- * Core git usually checks the working directory then the index,
- * except during a checkout when it checks the index first. It will
- * use index only for creating archives or for a bare repo (if an
- * index has been specified for the bare repo).
- */
-#define GIT_ATTR_CHECK_FILE_THEN_INDEX 0
-#define GIT_ATTR_CHECK_INDEX_THEN_FILE 1
-#define GIT_ATTR_CHECK_INDEX_ONLY 2
-
-/**
- * Check attribute flags: Using the system attributes file.
- *
- * Normally, attribute checks include looking in the /etc (or system
- * equivalent) directory for a `gitattributes` file. Passing this
- * flag will cause attribute checks to ignore that file.
- */
-#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
-
-/**
- * Look up the value of one git attribute for path.
- *
- * @param value_out Output of the value of the attribute. Use the GIT_ATTR_...
- * macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
- * use the string value for attributes set to a value. You
- * should NOT modify or free this value.
- * @param repo The repository containing the path.
- * @param flags A combination of GIT_ATTR_CHECK... flags.
- * @param path The path to check for attributes. Relative paths are
- * interpreted relative to the repo root. The file does
- * not have to exist, but if it does not, then it will be
- * treated as a plain file (not a directory).
- * @param name The name of the attribute to look up.
- */
-GIT_EXTERN(int) git_attr_get(
- const char **value_out,
- git_repository *repo,
- uint32_t flags,
- const char *path,
- const char *name);
-
-/**
- * Look up a list of git attributes for path.
- *
- * Use this if you have a known list of attributes that you want to
- * look up in a single call. This is somewhat more efficient than
- * calling `git_attr_get()` multiple times.
- *
- * For example, you might write:
- *
- * const char *attrs[] = { "crlf", "diff", "foo" };
- * const char **values[3];
- * git_attr_get_many(values, repo, 0, "my/fun/file.c", 3, attrs);
- *
- * Then you could loop through the 3 values to get the settings for
- * the three attributes you asked about.
- *
- * @param values_out An array of num_attr entries that will have string
- * pointers written into it for the values of the attributes.
- * You should not modify or free the values that are written
- * into this array (although of course, you should free the
- * array itself if you allocated it).
- * @param repo The repository containing the path.
- * @param flags A combination of GIT_ATTR_CHECK... flags.
- * @param path The path inside the repo to check attributes. This
- * does not have to exist, but if it does not, then
- * it will be treated as a plain file (i.e. not a directory).
- * @param num_attr The number of attributes being looked up
- * @param names An array of num_attr strings containing attribute names.
- */
-GIT_EXTERN(int) git_attr_get_many(
- const char **values_out,
- git_repository *repo,
- uint32_t flags,
- const char *path,
- size_t num_attr,
- const char **names);
-
-typedef int (*git_attr_foreach_cb)(const char *name, const char *value, void *payload);
-
-/**
- * Loop over all the git attributes for a path.
- *
- * @param repo The repository containing the path.
- * @param flags A combination of GIT_ATTR_CHECK... flags.
- * @param path Path inside the repo to check attributes. This does not have
- * to exist, but if it does not, then it will be treated as a
- * plain file (i.e. not a directory).
- * @param callback Function to invoke on each attribute name and value. The
- * value may be NULL is the attribute is explicitly set to
- * UNSPECIFIED using the '!' sign. Callback will be invoked
- * only once per attribute name, even if there are multiple
- * rules for a given file. The highest priority rule will be
- * used. Return a non-zero value from this to stop looping.
- * The value will be returned from `git_attr_foreach`.
- * @param payload Passed on as extra parameter to callback function.
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_attr_foreach(
- git_repository *repo,
- uint32_t flags,
- const char *path,
- git_attr_foreach_cb callback,
- void *payload);
-
-/**
- * Flush the gitattributes cache.
- *
- * Call this if you have reason to believe that the attributes files on
- * disk no longer match the cached contents of memory. This will cause
- * the attributes files to be reloaded the next time that an attribute
- * access function is called.
- */
-GIT_EXTERN(void) git_attr_cache_flush(
- git_repository *repo);
-
-/**
- * Add a macro definition.
- *
- * Macros will automatically be loaded from the top level `.gitattributes`
- * file of the repository (plus the build-in "binary" macro). This
- * function allows you to add others. For example, to add the default
- * macro, you would call:
- *
- * git_attr_add_macro(repo, "binary", "-diff -crlf");
- */
-GIT_EXTERN(int) git_attr_add_macro(
- git_repository *repo,
- const char *name,
- const char *values);
-
-/** @} */
-GIT_END_DECL
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_git_blame_h__
-#define INCLUDE_git_blame_h__
-
-#include "common.h"
-#include "oid.h"
-
-/**
- * @file git2/blame.h
- * @brief Git blame routines
- * @defgroup git_blame Git blame routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Flags for indicating option behavior for git_blame APIs.
- */
-typedef enum {
- /** Normal blame, the default */
- GIT_BLAME_NORMAL = 0,
- /** Track lines that have moved within a file (like `git blame -M`).
- * NOT IMPLEMENTED. */
- GIT_BLAME_TRACK_COPIES_SAME_FILE = (1<<0),
- /** Track lines that have moved across files in the same commit (like `git blame -C`).
- * NOT IMPLEMENTED. */
- GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = (1<<1),
- /** Track lines that have been copied from another file that exists in the
- * same commit (like `git blame -CC`). Implies SAME_FILE.
- * NOT IMPLEMENTED. */
- GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = (1<<2),
- /** Track lines that have been copied from another file that exists in *any*
- * commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
- * NOT IMPLEMENTED. */
- GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3),
- /** Restrict the search of commits to those reachable following only the
- * first parents. */
- GIT_BLAME_FIRST_PARENT = (1<<4),
-} git_blame_flag_t;
-
-/**
- * Blame options structure
- *
- * Use zeros to indicate default settings. It's easiest to use the
- * `GIT_BLAME_OPTIONS_INIT` macro:
- * git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
- *
- * - `flags` is a combination of the `git_blame_flag_t` values above.
- * - `min_match_characters` is the lower bound on the number of alphanumeric
- * characters that must be detected as moving/copying within a file for it to
- * associate those lines with the parent commit. The default value is 20.
- * This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*`
- * flags are specified.
- * - `newest_commit` is the id of the newest commit to consider. The default
- * is HEAD.
- * - `oldest_commit` is the id of the oldest commit to consider. The default
- * is the first commit encountered with a NULL parent.
- * - `min_line` is the first line in the file to blame. The default is 1 (line
- * numbers start with 1).
- * - `max_line` is the last line in the file to blame. The default is the last
- * line of the file.
- */
-typedef struct git_blame_options {
- unsigned int version;
-
- uint32_t flags;
- uint16_t min_match_characters;
- git_oid newest_commit;
- git_oid oldest_commit;
- size_t min_line;
- size_t max_line;
-} git_blame_options;
-
-#define GIT_BLAME_OPTIONS_VERSION 1
-#define GIT_BLAME_OPTIONS_INIT {GIT_BLAME_OPTIONS_VERSION}
-
-/**
- * Initializes a `git_blame_options` with default values. Equivalent to
- * creating an instance with GIT_BLAME_OPTIONS_INIT.
- *
- * @param opts The `git_blame_options` struct to initialize
- * @param version Version of struct; pass `GIT_BLAME_OPTIONS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_blame_init_options(
- git_blame_options *opts,
- unsigned int version);
-
-/**
- * Structure that represents a blame hunk.
- *
- * - `lines_in_hunk` is the number of lines in this hunk
- * - `final_commit_id` is the OID of the commit where this line was last
- * changed.
- * - `final_start_line_number` is the 1-based line number where this hunk
- * begins, in the final version of the file
- * - `orig_commit_id` is the OID of the commit where this hunk was found. This
- * will usually be the same as `final_commit_id`, except when
- * `GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES` has been specified.
- * - `orig_path` is the path to the file where this hunk originated, as of the
- * commit specified by `orig_commit_id`.
- * - `orig_start_line_number` is the 1-based line number where this hunk begins
- * in the file named by `orig_path` in the commit specified by
- * `orig_commit_id`.
- * - `boundary` is 1 iff the hunk has been tracked to a boundary commit (the
- * root, or the commit specified in git_blame_options.oldest_commit)
- */
-typedef struct git_blame_hunk {
- size_t lines_in_hunk;
-
- git_oid final_commit_id;
- size_t final_start_line_number;
- git_signature *final_signature;
-
- git_oid orig_commit_id;
- const char *orig_path;
- size_t orig_start_line_number;
- git_signature *orig_signature;
-
- char boundary;
-} git_blame_hunk;
-
-
-/* Opaque structure to hold blame results */
-typedef struct git_blame git_blame;
-
-/**
- * Gets the number of hunks that exist in the blame structure.
- */
-GIT_EXTERN(uint32_t) git_blame_get_hunk_count(git_blame *blame);
-
-/**
- * Gets the blame hunk at the given index.
- *
- * @param blame the blame structure to query
- * @param index index of the hunk to retrieve
- * @return the hunk at the given index, or NULL on error
- */
-GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex(
- git_blame *blame,
- uint32_t index);
-
-/**
- * Gets the hunk that relates to the given line number in the newest commit.
- *
- * @param blame the blame structure to query
- * @param lineno the (1-based) line number to find a hunk for
- * @return the hunk that contains the given line, or NULL on error
- */
-GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline(
- git_blame *blame,
- size_t lineno);
-
-/**
- * Get the blame for a single file.
- *
- * @param out pointer that will receive the blame object
- * @param repo repository whose history is to be walked
- * @param path path to file to consider
- * @param options options for the blame operation. If NULL, this is treated as
- * though GIT_BLAME_OPTIONS_INIT were passed.
- * @return 0 on success, or an error code. (use giterr_last for information
- * about the error.)
- */
-GIT_EXTERN(int) git_blame_file(
- git_blame **out,
- git_repository *repo,
- const char *path,
- git_blame_options *options);
-
-
-/**
- * Get blame data for a file that has been modified in memory. The `reference`
- * parameter is a pre-calculated blame for the in-odb history of the file. This
- * means that once a file blame is completed (which can be expensive), updating
- * the buffer blame is very fast.
- *
- * Lines that differ between the buffer and the committed version are marked as
- * having a zero OID for their final_commit_id.
- *
- * @param out pointer that will receive the resulting blame data
- * @param reference cached blame from the history of the file (usually the output
- * from git_blame_file)
- * @param buffer the (possibly) modified contents of the file
- * @param buffer_len number of valid bytes in the buffer
- * @return 0 on success, or an error code. (use giterr_last for information
- * about the error)
- */
-GIT_EXTERN(int) git_blame_buffer(
- git_blame **out,
- git_blame *reference,
- const char *buffer,
- size_t buffer_len);
-
-/**
- * Free memory allocated by git_blame_file or git_blame_buffer.
- *
- * @param blame the blame structure to free
- */
-GIT_EXTERN(void) git_blame_free(git_blame *blame);
-
-/** @} */
-GIT_END_DECL
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_blob_h__
-#define INCLUDE_git_blob_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "object.h"
-#include "buffer.h"
-
-/**
- * @file git2/blob.h
- * @brief Git blob load and write routines
- * @defgroup git_blob Git blob load and write routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Lookup a blob object from a repository.
- *
- * @param blob pointer to the looked up blob
- * @param repo the repo to use when locating the blob.
- * @param id identity of the blob to locate.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id);
-
-/**
- * Lookup a blob object from a repository,
- * given a prefix of its identifier (short id).
- *
- * @see git_object_lookup_prefix
- *
- * @param blob pointer to the looked up blob
- * @param repo the repo to use when locating the blob.
- * @param id identity of the blob to locate.
- * @param len the length of the short identifier
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len);
-
-/**
- * Close an open blob
- *
- * This is a wrapper around git_object_free()
- *
- * IMPORTANT:
- * It *is* necessary to call this method when you stop
- * using a blob. Failure to do so will cause a memory leak.
- *
- * @param blob the blob to close
- */
-GIT_EXTERN(void) git_blob_free(git_blob *blob);
-
-/**
- * Get the id of a blob.
- *
- * @param blob a previously loaded blob.
- * @return SHA1 hash for this blob.
- */
-GIT_EXTERN(const git_oid *) git_blob_id(const git_blob *blob);
-
-/**
- * Get the repository that contains the blob.
- *
- * @param blob A previously loaded blob.
- * @return Repository that contains this blob.
- */
-GIT_EXTERN(git_repository *) git_blob_owner(const git_blob *blob);
-
-/**
- * Get a read-only buffer with the raw content of a blob.
- *
- * A pointer to the raw content of a blob is returned;
- * this pointer is owned internally by the object and shall
- * not be free'd. The pointer may be invalidated at a later
- * time.
- *
- * @param blob pointer to the blob
- * @return the pointer
- */
-GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob);
-
-/**
- * Get the size in bytes of the contents of a blob
- *
- * @param blob pointer to the blob
- * @return size on bytes
- */
-GIT_EXTERN(git_off_t) git_blob_rawsize(const git_blob *blob);
-
-/**
- * Get a buffer with the filtered content of a blob.
- *
- * This applies filters as if the blob was being checked out to the
- * working directory under the specified filename. This may apply
- * CRLF filtering or other types of changes depending on the file
- * attributes set for the blob and the content detected in it.
- *
- * The output is written into a `git_buf` which the caller must free
- * when done (via `git_buf_free`).
- *
- * If no filters need to be applied, then the `out` buffer will just
- * be populated with a pointer to the raw content of the blob. In
- * that case, be careful to *not* free the blob until done with the
- * buffer or copy it into memory you own.
- *
- * @param out The git_buf to be filled in
- * @param blob Pointer to the blob
- * @param as_path Path used for file attribute lookups, etc.
- * @param check_for_binary_data Should this test if blob content contains
- * NUL bytes / looks like binary data before applying filters?
- * @return 0 on success or an error code
- */
-GIT_EXTERN(int) git_blob_filtered_content(
- git_buf *out,
- git_blob *blob,
- const char *as_path,
- int check_for_binary_data);
-
-/**
- * Read a file from the working folder of a repository
- * and write it to the Object Database as a loose blob
- *
- * @param id return the id of the written blob
- * @param repo repository where the blob will be written.
- * this repository cannot be bare
- * @param relative_path file from which the blob will be created,
- * relative to the repository's working dir
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_blob_create_fromworkdir(git_oid *id, git_repository *repo, const char *relative_path);
-
-/**
- * Read a file from the filesystem and write its content
- * to the Object Database as a loose blob
- *
- * @param id return the id of the written blob
- * @param repo repository where the blob will be written.
- * this repository can be bare or not
- * @param path file from which the blob will be created
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *id, git_repository *repo, const char *path);
-
-/**
- * Create a stream to write a new blob into the object db
- *
- * This function may need to buffer the data on disk and will in
- * general not be the right choice if you know the size of the data
- * to write. If you have data in memory, use
- * `git_blob_create_frombuffer()`. If you do not, but know the size of
- * the contents (and don't want/need to perform filtering), use
- * `git_odb_open_wstream()`.
- *
- * Don't close this stream yourself but pass it to
- * `git_blob_create_fromstream_commit()` to commit the write to the
- * object db and get the object id.
- *
- * If the `hintpath` parameter is filled, it will be used to determine
- * what git filters should be applied to the object before it is written
- * to the object database.
- *
- * @param out the stream into which to write
- * @param repo Repository where the blob will be written.
- * This repository can be bare or not.
- * @param hintpath If not NULL, will be used to select data filters
- * to apply onto the content of the blob to be created.
- * @return 0 or error code
- */
-GIT_EXTERN(int) git_blob_create_fromstream(
- git_writestream **out,
- git_repository *repo,
- const char *hintpath);
-
-/**
- * Close the stream and write the blob to the object db
- *
- * The stream will be closed and freed.
- *
- * @param out the id of the new blob
- * @param stream the stream to close
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_blob_create_fromstream_commit(
- git_oid *out,
- git_writestream *stream);
-
-/**
- * Write an in-memory buffer to the ODB as a blob
- *
- * @param id return the id of the written blob
- * @param repo repository where to blob will be written
- * @param buffer data to be written into the blob
- * @param len length of the data
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_blob_create_frombuffer(
- git_oid *id, git_repository *repo, const void *buffer, size_t len);
-
-/**
- * Determine if the blob content is most certainly binary or not.
- *
- * The heuristic used to guess if a file is binary is taken from core git:
- * Searching for NUL bytes and looking for a reasonable ratio of printable
- * to non-printable characters among the first 8000 bytes.
- *
- * @param blob The blob which content should be analyzed
- * @return 1 if the content of the blob is detected
- * as binary; 0 otherwise.
- */
-GIT_EXTERN(int) git_blob_is_binary(const git_blob *blob);
-
-/**
- * Create an in-memory copy of a blob. The copy must be explicitly
- * free'd or it will leak.
- *
- * @param out Pointer to store the copy of the object
- * @param source Original object to copy
- */
-GIT_EXTERN(int) git_blob_dup(git_blob **out, git_blob *source);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_branch_h__
-#define INCLUDE_git_branch_h__
-
-#include "common.h"
-#include "oid.h"
-#include "types.h"
-
-/**
- * @file git2/branch.h
- * @brief Git branch parsing routines
- * @defgroup git_branch Git branch management
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Create a new branch pointing at a target commit
- *
- * A new direct reference will be created pointing to
- * this target commit. If `force` is true and a reference
- * already exists with the given name, it'll be replaced.
- *
- * The returned reference must be freed by the user.
- *
- * The branch name will be checked for validity.
- * See `git_tag_create()` for rules about valid names.
- *
- * @param out Pointer where to store the underlying reference.
- *
- * @param branch_name Name for the branch; this name is
- * validated for consistency. It should also not conflict with
- * an already existing branch name.
- *
- * @param target Commit to which this branch should point. This object
- * must belong to the given `repo`.
- *
- * @param force Overwrite existing branch.
- *
- * @return 0, GIT_EINVALIDSPEC or an error code.
- * A proper reference is written in the refs/heads namespace
- * pointing to the provided target commit.
- */
-GIT_EXTERN(int) git_branch_create(
- git_reference **out,
- git_repository *repo,
- const char *branch_name,
- const git_commit *target,
- int force);
-
-/**
- * Create a new branch pointing at a target commit
- *
- * This behaves like `git_branch_create()` but takes an annotated
- * commit, which lets you specify which extended sha syntax string was
- * specified by a user, allowing for more exact reflog messages.
- *
- * See the documentation for `git_branch_create()`.
- *
- * @see git_branch_create
- */
-GIT_EXTERN(int) git_branch_create_from_annotated(
- git_reference **ref_out,
- git_repository *repository,
- const char *branch_name,
- const git_annotated_commit *commit,
- int force);
-
-/**
- * Delete an existing branch reference.
- *
- * If the branch is successfully deleted, the passed reference
- * object will be invalidated. The reference must be freed manually
- * by the user.
- *
- * @param branch A valid reference representing a branch
- * @return 0 on success, or an error code.
- */
-GIT_EXTERN(int) git_branch_delete(git_reference *branch);
-
-/** Iterator type for branches */
-typedef struct git_branch_iterator git_branch_iterator;
-
-/**
- * Create an iterator which loops over the requested branches.
- *
- * @param out the iterator
- * @param repo Repository where to find the branches.
- * @param list_flags Filtering flags for the branch
- * listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE
- * or GIT_BRANCH_ALL.
- *
- * @return 0 on success or an error code
- */
-GIT_EXTERN(int) git_branch_iterator_new(
- git_branch_iterator **out,
- git_repository *repo,
- git_branch_t list_flags);
-
-/**
- * Retrieve the next branch from the iterator
- *
- * @param out the reference
- * @param out_type the type of branch (local or remote-tracking)
- * @param iter the branch iterator
- * @return 0 on success, GIT_ITEROVER if there are no more branches or an error code.
- */
-GIT_EXTERN(int) git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *iter);
-
-/**
- * Free a branch iterator
- *
- * @param iter the iterator to free
- */
-GIT_EXTERN(void) git_branch_iterator_free(git_branch_iterator *iter);
-
-/**
- * Move/rename an existing local branch reference.
- *
- * The new branch name will be checked for validity.
- * See `git_tag_create()` for rules about valid names.
- *
- * @param branch Current underlying reference of the branch.
- *
- * @param new_branch_name Target name of the branch once the move
- * is performed; this name is validated for consistency.
- *
- * @param force Overwrite existing branch.
- *
- * @return 0 on success, GIT_EINVALIDSPEC or an error code.
- */
-GIT_EXTERN(int) git_branch_move(
- git_reference **out,
- git_reference *branch,
- const char *new_branch_name,
- int force);
-
-/**
- * Lookup a branch by its name in a repository.
- *
- * The generated reference must be freed by the user.
- *
- * The branch name will be checked for validity.
- * See `git_tag_create()` for rules about valid names.
- *
- * @param out pointer to the looked-up branch reference
- *
- * @param repo the repository to look up the branch
- *
- * @param branch_name Name of the branch to be looked-up;
- * this name is validated for consistency.
- *
- * @param branch_type Type of the considered branch. This should
- * be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE.
- *
- * @return 0 on success; GIT_ENOTFOUND when no matching branch
- * exists, GIT_EINVALIDSPEC, otherwise an error code.
- */
-GIT_EXTERN(int) git_branch_lookup(
- git_reference **out,
- git_repository *repo,
- const char *branch_name,
- git_branch_t branch_type);
-
-/**
- * Return the name of the given local or remote branch.
- *
- * The name of the branch matches the definition of the name
- * for git_branch_lookup. That is, if the returned name is given
- * to git_branch_lookup() then the reference is returned that
- * was given to this function.
- *
- * @param out where the pointer of branch name is stored;
- * this is valid as long as the ref is not freed.
- * @param ref the reference ideally pointing to a branch
- *
- * @return 0 on success; otherwise an error code (e.g., if the
- * ref is no local or remote branch).
- */
-GIT_EXTERN(int) git_branch_name(
- const char **out,
- const git_reference *ref);
-
-/**
- * Return the reference supporting the remote tracking branch,
- * given a local branch reference.
- *
- * @param out Pointer where to store the retrieved
- * reference.
- *
- * @param branch Current underlying reference of the branch.
- *
- * @return 0 on success; GIT_ENOTFOUND when no remote tracking
- * reference exists, otherwise an error code.
- */
-GIT_EXTERN(int) git_branch_upstream(
- git_reference **out,
- const git_reference *branch);
-
-/**
- * Set the upstream configuration for a given local branch
- *
- * @param branch the branch to configure
- *
- * @param upstream_name remote-tracking or local branch to set as
- * upstream. Pass NULL to unset.
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_branch_set_upstream(git_reference *branch, const char *upstream_name);
-
-/**
- * Return the name of the reference supporting the remote tracking branch,
- * given the name of a local branch reference.
- *
- * @param out Pointer to the user-allocated git_buf which will be
- * filled with the name of the reference.
- *
- * @param repo the repository where the branches live
- *
- * @param refname reference name of the local branch.
- *
- * @return 0, GIT_ENOTFOUND when no remote tracking reference exists,
- * otherwise an error code.
- */
-GIT_EXTERN(int) git_branch_upstream_name(
- git_buf *out,
- git_repository *repo,
- const char *refname);
-
-/**
- * Determine if the current local branch is pointed at by HEAD.
- *
- * @param branch Current underlying reference of the branch.
- *
- * @return 1 if HEAD points at the branch, 0 if it isn't,
- * error code otherwise.
- */
-GIT_EXTERN(int) git_branch_is_head(
- const git_reference *branch);
-
-/**
- * Return the name of remote that the remote tracking branch belongs to.
- *
- * @param out Pointer to the user-allocated git_buf which will be filled with the name of the remote.
- *
- * @param repo The repository where the branch lives.
- *
- * @param canonical_branch_name name of the remote tracking branch.
- *
- * @return 0, GIT_ENOTFOUND
- * when no remote matching remote was found,
- * GIT_EAMBIGUOUS when the branch maps to several remotes,
- * otherwise an error code.
- */
-GIT_EXTERN(int) git_branch_remote_name(
- git_buf *out,
- git_repository *repo,
- const char *canonical_branch_name);
-
-
-/**
- * Retrieve the name fo the upstream remote of a local branch
- *
- * @param buf the buffer into which to write the name
- * @param repo the repository in which to look
- * @param refname the full name of the branch
- * @return 0 or an error code
- */
- GIT_EXTERN(int) git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_buf_h__
-#define INCLUDE_git_buf_h__
-
-#include "common.h"
-
-/**
- * @file git2/buffer.h
- * @brief Buffer export structure
- *
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * A data buffer for exporting data from libgit2
- *
- * Sometimes libgit2 wants to return an allocated data buffer to the
- * caller and have the caller take responsibility for freeing that memory.
- * This can be awkward if the caller does not have easy access to the same
- * allocation functions that libgit2 is using. In those cases, libgit2
- * will fill in a `git_buf` and the caller can use `git_buf_free()` to
- * release it when they are done.
- *
- * A `git_buf` may also be used for the caller to pass in a reference to
- * a block of memory they hold. In this case, libgit2 will not resize or
- * free the memory, but will read from it as needed.
- *
- * A `git_buf` is a public structure with three fields:
- *
- * - `ptr` points to the start of the allocated memory. If it is NULL,
- * then the `git_buf` is considered empty and libgit2 will feel free
- * to overwrite it with new data.
- *
- * - `size` holds the size (in bytes) of the data that is actually used.
- *
- * - `asize` holds the known total amount of allocated memory if the `ptr`
- * was allocated by libgit2. It may be larger than `size`. If `ptr`
- * was not allocated by libgit2 and should not be resized and/or freed,
- * then `asize` will be set to zero.
- *
- * Some APIs may occasionally do something slightly unusual with a buffer,
- * such as setting `ptr` to a value that was passed in by the user. In
- * those cases, the behavior will be clearly documented by the API.
- */
-typedef struct {
- char *ptr;
- size_t asize, size;
-} git_buf;
-
-/**
- * Static initializer for git_buf from static buffer
- */
-#define GIT_BUF_INIT_CONST(STR,LEN) { (char *)(STR), 0, (size_t)(LEN) }
-
-/**
- * Free the memory referred to by the git_buf.
- *
- * Note that this does not free the `git_buf` itself, just the memory
- * pointed to by `buffer->ptr`. This will not free the memory if it looks
- * like it was not allocated internally, but it will clear the buffer back
- * to the empty state.
- *
- * @param buffer The buffer to deallocate
- */
-GIT_EXTERN(void) git_buf_free(git_buf *buffer);
-
-/**
- * Resize the buffer allocation to make more space.
- *
- * This will attempt to grow the buffer to accommodate the target size.
- *
- * If the buffer refers to memory that was not allocated by libgit2 (i.e.
- * the `asize` field is zero), then `ptr` will be replaced with a newly
- * allocated block of data. Be careful so that memory allocated by the
- * caller is not lost. As a special variant, if you pass `target_size` as
- * 0 and the memory is not allocated by libgit2, this will allocate a new
- * buffer of size `size` and copy the external data into it.
- *
- * Currently, this will never shrink a buffer, only expand it.
- *
- * If the allocation fails, this will return an error and the buffer will be
- * marked as invalid for future operations, invaliding the contents.
- *
- * @param buffer The buffer to be resized; may or may not be allocated yet
- * @param target_size The desired available size
- * @return 0 on success, -1 on allocation failure
- */
-GIT_EXTERN(int) git_buf_grow(git_buf *buffer, size_t target_size);
-
-/**
- * Set buffer to a copy of some raw data.
- *
- * @param buffer The buffer to set
- * @param data The data to copy into the buffer
- * @param datalen The length of the data to copy into the buffer
- * @return 0 on success, -1 on allocation failure
- */
-GIT_EXTERN(int) git_buf_set(
- git_buf *buffer, const void *data, size_t datalen);
-
-/**
-* Check quickly if buffer looks like it contains binary data
-*
-* @param buf Buffer to check
-* @return 1 if buffer looks like non-text data
-*/
-GIT_EXTERN(int) git_buf_is_binary(const git_buf *buf);
-
-/**
-* Check quickly if buffer contains a NUL byte
-*
-* @param buf Buffer to check
-* @return 1 if buffer contains a NUL byte
-*/
-GIT_EXTERN(int) git_buf_contains_nul(const git_buf *buf);
-
-GIT_END_DECL
-
-/** @} */
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_checkout_h__
-#define INCLUDE_git_checkout_h__
-
-#include "common.h"
-#include "types.h"
-#include "diff.h"
-
-/**
- * @file git2/checkout.h
- * @brief Git checkout routines
- * @defgroup git_checkout Git checkout routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Checkout behavior flags
- *
- * In libgit2, checkout is used to update the working directory and index
- * to match a target tree. Unlike git checkout, it does not move the HEAD
- * commit for you - use `git_repository_set_head` or the like to do that.
- *
- * Checkout looks at (up to) four things: the "target" tree you want to
- * check out, the "baseline" tree of what was checked out previously, the
- * working directory for actual files, and the index for staged changes.
- *
- * You give checkout one of three strategies for update:
- *
- * - `GIT_CHECKOUT_NONE` is a dry-run strategy that checks for conflicts,
- * etc., but doesn't make any actual changes.
- *
- * - `GIT_CHECKOUT_FORCE` is at the opposite extreme, taking any action to
- * make the working directory match the target (including potentially
- * discarding modified files).
- *
- * - `GIT_CHECKOUT_SAFE` is between these two options, it will only make
- * modifications that will not lose changes.
- *
- * | target == baseline | target != baseline |
- * ---------------------|-----------------------|----------------------|
- * workdir == baseline | no action | create, update, or |
- * | | delete file |
- * ---------------------|-----------------------|----------------------|
- * workdir exists and | no action | conflict (notify |
- * is != baseline | notify dirty MODIFIED | and cancel checkout) |
- * ---------------------|-----------------------|----------------------|
- * workdir missing, | notify dirty DELETED | create file |
- * baseline present | | |
- * ---------------------|-----------------------|----------------------|
- *
- * To emulate `git checkout`, use `GIT_CHECKOUT_SAFE` with a checkout
- * notification callback (see below) that displays information about dirty
- * files. The default behavior will cancel checkout on conflicts.
- *
- * To emulate `git checkout-index`, use `GIT_CHECKOUT_SAFE` with a
- * notification callback that cancels the operation if a dirty-but-existing
- * file is found in the working directory. This core git command isn't
- * quite "force" but is sensitive about some types of changes.
- *
- * To emulate `git checkout -f`, use `GIT_CHECKOUT_FORCE`.
- *
- *
- * There are some additional flags to modified the behavior of checkout:
- *
- * - GIT_CHECKOUT_ALLOW_CONFLICTS makes SAFE mode apply safe file updates
- * even if there are conflicts (instead of cancelling the checkout).
- *
- * - GIT_CHECKOUT_REMOVE_UNTRACKED means remove untracked files (i.e. not
- * in target, baseline, or index, and not ignored) from the working dir.
- *
- * - GIT_CHECKOUT_REMOVE_IGNORED means remove ignored files (that are also
- * untracked) from the working directory as well.
- *
- * - GIT_CHECKOUT_UPDATE_ONLY means to only update the content of files that
- * already exist. Files will not be created nor deleted. This just skips
- * applying adds, deletes, and typechanges.
- *
- * - GIT_CHECKOUT_DONT_UPDATE_INDEX prevents checkout from writing the
- * updated files' information to the index.
- *
- * - Normally, checkout will reload the index and git attributes from disk
- * before any operations. GIT_CHECKOUT_NO_REFRESH prevents this reload.
- *
- * - Unmerged index entries are conflicts. GIT_CHECKOUT_SKIP_UNMERGED skips
- * files with unmerged index entries instead. GIT_CHECKOUT_USE_OURS and
- * GIT_CHECKOUT_USE_THEIRS to proceed with the checkout using either the
- * stage 2 ("ours") or stage 3 ("theirs") version of files in the index.
- *
- * - GIT_CHECKOUT_DONT_OVERWRITE_IGNORED prevents ignored files from being
- * overwritten. Normally, files that are ignored in the working directory
- * are not considered "precious" and may be overwritten if the checkout
- * target contains that file.
- *
- * - GIT_CHECKOUT_DONT_REMOVE_EXISTING prevents checkout from removing
- * files or folders that fold to the same name on case insensitive
- * filesystems. This can cause files to retain their existing names
- * and write through existing symbolic links.
- */
-typedef enum {
- GIT_CHECKOUT_NONE = 0, /**< default is a dry run, no actual updates */
-
- /** Allow safe updates that cannot overwrite uncommitted data */
- GIT_CHECKOUT_SAFE = (1u << 0),
-
- /** Allow all updates to force working directory to look like index */
- GIT_CHECKOUT_FORCE = (1u << 1),
-
-
- /** Allow checkout to recreate missing files */
- GIT_CHECKOUT_RECREATE_MISSING = (1u << 2),
-
- /** Allow checkout to make safe updates even if conflicts are found */
- GIT_CHECKOUT_ALLOW_CONFLICTS = (1u << 4),
-
- /** Remove untracked files not in index (that are not ignored) */
- GIT_CHECKOUT_REMOVE_UNTRACKED = (1u << 5),
-
- /** Remove ignored files not in index */
- GIT_CHECKOUT_REMOVE_IGNORED = (1u << 6),
-
- /** Only update existing files, don't create new ones */
- GIT_CHECKOUT_UPDATE_ONLY = (1u << 7),
-
- /**
- * Normally checkout updates index entries as it goes; this stops that.
- * Implies `GIT_CHECKOUT_DONT_WRITE_INDEX`.
- */
- GIT_CHECKOUT_DONT_UPDATE_INDEX = (1u << 8),
-
- /** Don't refresh index/config/etc before doing checkout */
- GIT_CHECKOUT_NO_REFRESH = (1u << 9),
-
- /** Allow checkout to skip unmerged files */
- GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
- /** For unmerged files, checkout stage 2 from index */
- GIT_CHECKOUT_USE_OURS = (1u << 11),
- /** For unmerged files, checkout stage 3 from index */
- GIT_CHECKOUT_USE_THEIRS = (1u << 12),
-
- /** Treat pathspec as simple list of exact match file paths */
- GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13),
-
- /** Ignore directories in use, they will be left empty */
- GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES = (1u << 18),
-
- /** Don't overwrite ignored files that exist in the checkout target */
- GIT_CHECKOUT_DONT_OVERWRITE_IGNORED = (1u << 19),
-
- /** Write normal merge files for conflicts */
- GIT_CHECKOUT_CONFLICT_STYLE_MERGE = (1u << 20),
-
- /** Include common ancestor data in diff3 format files for conflicts */
- GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 = (1u << 21),
-
- /** Don't overwrite existing files or folders */
- GIT_CHECKOUT_DONT_REMOVE_EXISTING = (1u << 22),
-
- /** Normally checkout writes the index upon completion; this prevents that. */
- GIT_CHECKOUT_DONT_WRITE_INDEX = (1u << 23),
-
- /**
- * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
- */
-
- /** Recursively checkout submodules with same options (NOT IMPLEMENTED) */
- GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16),
- /** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */
- GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = (1u << 17),
-
-} git_checkout_strategy_t;
-
-/**
- * Checkout notification flags
- *
- * Checkout will invoke an options notification callback (`notify_cb`) for
- * certain cases - you pick which ones via `notify_flags`:
- *
- * - GIT_CHECKOUT_NOTIFY_CONFLICT invokes checkout on conflicting paths.
- *
- * - GIT_CHECKOUT_NOTIFY_DIRTY notifies about "dirty" files, i.e. those that
- * do not need an update but no longer match the baseline. Core git
- * displays these files when checkout runs, but won't stop the checkout.
- *
- * - GIT_CHECKOUT_NOTIFY_UPDATED sends notification for any file changed.
- *
- * - GIT_CHECKOUT_NOTIFY_UNTRACKED notifies about untracked files.
- *
- * - GIT_CHECKOUT_NOTIFY_IGNORED notifies about ignored files.
- *
- * Returning a non-zero value from this callback will cancel the checkout.
- * The non-zero return value will be propagated back and returned by the
- * git_checkout_... call.
- *
- * Notification callbacks are made prior to modifying any files on disk,
- * so canceling on any notification will still happen prior to any files
- * being modified.
- */
-typedef enum {
- GIT_CHECKOUT_NOTIFY_NONE = 0,
- GIT_CHECKOUT_NOTIFY_CONFLICT = (1u << 0),
- GIT_CHECKOUT_NOTIFY_DIRTY = (1u << 1),
- GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2),
- GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3),
- GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4),
-
- GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFFu
-} git_checkout_notify_t;
-
-typedef struct {
- size_t mkdir_calls;
- size_t stat_calls;
- size_t chmod_calls;
-} git_checkout_perfdata;
-
-/** Checkout notification callback function */
-typedef int (*git_checkout_notify_cb)(
- git_checkout_notify_t why,
- const char *path,
- const git_diff_file *baseline,
- const git_diff_file *target,
- const git_diff_file *workdir,
- void *payload);
-
-/** Checkout progress notification function */
-typedef void (*git_checkout_progress_cb)(
- const char *path,
- size_t completed_steps,
- size_t total_steps,
- void *payload);
-
-/** Checkout perfdata notification function */
-typedef void (*git_checkout_perfdata_cb)(
- const git_checkout_perfdata *perfdata,
- void *payload);
-
-/**
- * Checkout options structure
- *
- * Zero out for defaults. Initialize with `GIT_CHECKOUT_OPTIONS_INIT` macro to
- * correctly set the `version` field. E.g.
- *
- * git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
- */
-typedef struct git_checkout_options {
- unsigned int version;
-
- unsigned int checkout_strategy; /**< default will be a dry run */
-
- int disable_filters; /**< don't apply filters like CRLF conversion */
- unsigned int dir_mode; /**< default is 0755 */
- unsigned int file_mode; /**< default is 0644 or 0755 as dictated by blob */
- int file_open_flags; /**< default is O_CREAT | O_TRUNC | O_WRONLY */
-
- unsigned int notify_flags; /**< see `git_checkout_notify_t` above */
- git_checkout_notify_cb notify_cb;
- void *notify_payload;
-
- /** Optional callback to notify the consumer of checkout progress. */
- git_checkout_progress_cb progress_cb;
- void *progress_payload;
-
- /** When not zeroed out, array of fnmatch patterns specifying which
- * paths should be taken into account, otherwise all files. Use
- * GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH to treat as simple list.
- */
- git_strarray paths;
-
- /** The expected content of the working directory; defaults to HEAD.
- * If the working directory does not match this baseline information,
- * that will produce a checkout conflict.
- */
- git_tree *baseline;
-
- /** Like `baseline` above, though expressed as an index. This
- * option overrides `baseline`.
- */
- git_index *baseline_index; /**< expected content of workdir, expressed as an index. */
-
- const char *target_directory; /**< alternative checkout path to workdir */
-
- const char *ancestor_label; /**< the name of the common ancestor side of conflicts */
- const char *our_label; /**< the name of the "our" side of conflicts */
- const char *their_label; /**< the name of the "their" side of conflicts */
-
- /** Optional callback to notify the consumer of performance data. */
- git_checkout_perfdata_cb perfdata_cb;
- void *perfdata_payload;
-} git_checkout_options;
-
-#define GIT_CHECKOUT_OPTIONS_VERSION 1
-#define GIT_CHECKOUT_OPTIONS_INIT {GIT_CHECKOUT_OPTIONS_VERSION}
-
-/**
-* Initializes a `git_checkout_options` with default values. Equivalent to
-* creating an instance with GIT_CHECKOUT_OPTIONS_INIT.
-*
-* @param opts the `git_checkout_options` struct to initialize.
-* @param version Version of struct; pass `GIT_CHECKOUT_OPTIONS_VERSION`
-* @return Zero on success; -1 on failure.
-*/
-GIT_EXTERN(int) git_checkout_init_options(
- git_checkout_options *opts,
- unsigned int version);
-
-/**
- * Updates files in the index and the working tree to match the content of
- * the commit pointed at by HEAD.
- *
- * Note that this is _not_ the correct mechanism used to switch branches;
- * do not change your `HEAD` and then call this method, that would leave
- * you with checkout conflicts since your working directory would then
- * appear to be dirty. Instead, checkout the target of the branch and
- * then update `HEAD` using `git_repository_set_head` to point to the
- * branch you checked out.
- *
- * @param repo repository to check out (must be non-bare)
- * @param opts specifies checkout options (may be NULL)
- * @return 0 on success, GIT_EUNBORNBRANCH if HEAD points to a non
- * existing branch, non-zero value returned by `notify_cb`, or
- * other error code < 0 (use giterr_last for error details)
- */
-GIT_EXTERN(int) git_checkout_head(
- git_repository *repo,
- const git_checkout_options *opts);
-
-/**
- * Updates files in the working tree to match the content of the index.
- *
- * @param repo repository into which to check out (must be non-bare)
- * @param index index to be checked out (or NULL to use repository index)
- * @param opts specifies checkout options (may be NULL)
- * @return 0 on success, non-zero return value from `notify_cb`, or error
- * code < 0 (use giterr_last for error details)
- */
-GIT_EXTERN(int) git_checkout_index(
- git_repository *repo,
- git_index *index,
- const git_checkout_options *opts);
-
-/**
- * Updates files in the index and working tree to match the content of the
- * tree pointed at by the treeish.
- *
- * @param repo repository to check out (must be non-bare)
- * @param treeish a commit, tag or tree which content will be used to update
- * the working directory (or NULL to use HEAD)
- * @param opts specifies checkout options (may be NULL)
- * @return 0 on success, non-zero return value from `notify_cb`, or error
- * code < 0 (use giterr_last for error details)
- */
-GIT_EXTERN(int) git_checkout_tree(
- git_repository *repo,
- const git_object *treeish,
- const git_checkout_options *opts);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_cherrypick_h__
-#define INCLUDE_git_cherrypick_h__
-
-#include "common.h"
-#include "types.h"
-#include "merge.h"
-
-/**
- * @file git2/cherrypick.h
- * @brief Git cherry-pick routines
- * @defgroup git_cherrypick Git cherry-pick routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Cherry-pick options
- */
-typedef struct {
- unsigned int version;
-
- /** For merge commits, the "mainline" is treated as the parent. */
- unsigned int mainline;
-
- git_merge_options merge_opts; /**< Options for the merging */
- git_checkout_options checkout_opts; /**< Options for the checkout */
-} git_cherrypick_options;
-
-#define GIT_CHERRYPICK_OPTIONS_VERSION 1
-#define GIT_CHERRYPICK_OPTIONS_INIT {GIT_CHERRYPICK_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT}
-
-/**
- * Initializes a `git_cherrypick_options` with default values. Equivalent to
- * creating an instance with GIT_CHERRYPICK_OPTIONS_INIT.
- *
- * @param opts the `git_cherrypick_options` struct to initialize
- * @param version Version of struct; pass `GIT_CHERRYPICK_OPTIONS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_cherrypick_init_options(
- git_cherrypick_options *opts,
- unsigned int version);
-
-/**
- * Cherry-picks the given commit against the given "our" commit, producing an
- * index that reflects the result of the cherry-pick.
- *
- * The returned index must be freed explicitly with `git_index_free`.
- *
- * @param out pointer to store the index result in
- * @param repo the repository that contains the given commits
- * @param cherrypick_commit the commit to cherry-pick
- * @param our_commit the commit to revert against (eg, HEAD)
- * @param mainline the parent of the revert commit, if it is a merge
- * @param merge_options the merge options (or null for defaults)
- * @return zero on success, -1 on failure.
- */
-GIT_EXTERN(int) git_cherrypick_commit(
- git_index **out,
- git_repository *repo,
- git_commit *cherrypick_commit,
- git_commit *our_commit,
- unsigned int mainline,
- const git_merge_options *merge_options);
-
-/**
- * Cherry-pick the given commit, producing changes in the index and working directory.
- *
- * @param repo the repository to cherry-pick
- * @param commit the commit to cherry-pick
- * @param cherrypick_options the cherry-pick options (or null for defaults)
- * @return zero on success, -1 on failure.
- */
-GIT_EXTERN(int) git_cherrypick(
- git_repository *repo,
- git_commit *commit,
- const git_cherrypick_options *cherrypick_options);
-
-/** @} */
-GIT_END_DECL
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_clone_h__
-#define INCLUDE_git_clone_h__
-
-#include "common.h"
-#include "types.h"
-#include "indexer.h"
-#include "checkout.h"
-#include "remote.h"
-#include "transport.h"
-
-
-/**
- * @file git2/clone.h
- * @brief Git cloning routines
- * @defgroup git_clone Git cloning routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Options for bypassing the git-aware transport on clone. Bypassing
- * it means that instead of a fetch, libgit2 will copy the object
- * database directory instead of figuring out what it needs, which is
- * faster. If possible, it will hardlink the files to save space.
- */
-typedef enum {
- /**
- * Auto-detect (default), libgit2 will bypass the git-aware
- * transport for local paths, but use a normal fetch for
- * `file://` urls.
- */
- GIT_CLONE_LOCAL_AUTO,
- /**
- * Bypass the git-aware transport even for a `file://` url.
- */
- GIT_CLONE_LOCAL,
- /**
- * Do no bypass the git-aware transport
- */
- GIT_CLONE_NO_LOCAL,
- /**
- * Bypass the git-aware transport, but do not try to use
- * hardlinks.
- */
- GIT_CLONE_LOCAL_NO_LINKS,
-} git_clone_local_t;
-
-/**
- * The signature of a function matching git_remote_create, with an additional
- * void* as a callback payload.
- *
- * Callers of git_clone may provide a function matching this signature to override
- * the remote creation and customization process during a clone operation.
- *
- * @param out the resulting remote
- * @param repo the repository in which to create the remote
- * @param name the remote's name
- * @param url the remote's url
- * @param payload an opaque payload
- * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
- */
-typedef int (*git_remote_create_cb)(
- git_remote **out,
- git_repository *repo,
- const char *name,
- const char *url,
- void *payload);
-
-/**
- * The signature of a function matchin git_repository_init, with an
- * aditional void * as callback payload.
- *
- * Callers of git_clone my provide a function matching this signature
- * to override the repository creation and customization process
- * during a clone operation.
- *
- * @param out the resulting repository
- * @param path path in which to create the repository
- * @param bare whether the repository is bare. This is the value from the clone options
- * @param payload payload specified by the options
- * @return 0, or a negative value to indicate error
- */
-typedef int (*git_repository_create_cb)(
- git_repository **out,
- const char *path,
- int bare,
- void *payload);
-
-/**
- * Clone options structure
- *
- * Use the GIT_CLONE_OPTIONS_INIT to get the default settings, like this:
- *
- * git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
- */
-typedef struct git_clone_options {
- unsigned int version;
-
- /**
- * These options are passed to the checkout step. To disable
- * checkout, set the `checkout_strategy` to
- * `GIT_CHECKOUT_NONE`.
- */
- git_checkout_options checkout_opts;
-
- /**
- * Options which control the fetch, including callbacks.
- *
- * The callbacks are used for reporting fetch progress, and for acquiring
- * credentials in the event they are needed.
- */
- git_fetch_options fetch_opts;
-
- /**
- * Set to zero (false) to create a standard repo, or non-zero
- * for a bare repo
- */
- int bare;
-
- /**
- * Whether to use a fetch or copy the object database.
- */
- git_clone_local_t local;
-
- /**
- * The name of the branch to checkout. NULL means use the
- * remote's default branch.
- */
- const char* checkout_branch;
-
- /**
- * A callback used to create the new repository into which to
- * clone. If NULL, the 'bare' field will be used to determine
- * whether to create a bare repository.
- */
- git_repository_create_cb repository_cb;
-
- /**
- * An opaque payload to pass to the git_repository creation callback.
- * This parameter is ignored unless repository_cb is non-NULL.
- */
- void *repository_cb_payload;
-
- /**
- * A callback used to create the git_remote, prior to its being
- * used to perform the clone operation. See the documentation for
- * git_remote_create_cb for details. This parameter may be NULL,
- * indicating that git_clone should provide default behavior.
- */
- git_remote_create_cb remote_cb;
-
- /**
- * An opaque payload to pass to the git_remote creation callback.
- * This parameter is ignored unless remote_cb is non-NULL.
- */
- void *remote_cb_payload;
-} git_clone_options;
-
-#define GIT_CLONE_OPTIONS_VERSION 1
-#define GIT_CLONE_OPTIONS_INIT { GIT_CLONE_OPTIONS_VERSION, \
- { GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \
- GIT_FETCH_OPTIONS_INIT }
-
-/**
- * Initializes a `git_clone_options` with default values. Equivalent to
- * creating an instance with GIT_CLONE_OPTIONS_INIT.
- *
- * @param opts The `git_clone_options` struct to initialize
- * @param version Version of struct; pass `GIT_CLONE_OPTIONS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_clone_init_options(
- git_clone_options *opts,
- unsigned int version);
-
-/**
- * Clone a remote repository.
- *
- * By default this creates its repository and initial remote to match
- * git's defaults. You can use the options in the callback to
- * customize how these are created.
- *
- * @param out pointer that will receive the resulting repository object
- * @param url the remote repository to clone
- * @param local_path local directory to clone to
- * @param options configuration options for the clone. If NULL, the
- * function works as though GIT_OPTIONS_INIT were passed.
- * @return 0 on success, any non-zero return value from a callback
- * function, or a negative value to indicate an error (use
- * `giterr_last` for a detailed error message)
- */
-GIT_EXTERN(int) git_clone(
- git_repository **out,
- const char *url,
- const char *local_path,
- const git_clone_options *options);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_commit_h__
-#define INCLUDE_git_commit_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "object.h"
-
-/**
- * @file git2/commit.h
- * @brief Git commit parsing, formatting routines
- * @defgroup git_commit Git commit parsing, formatting routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Lookup a commit object from a repository.
- *
- * The returned object should be released with `git_commit_free` when no
- * longer needed.
- *
- * @param commit pointer to the looked up commit
- * @param repo the repo to use when locating the commit.
- * @param id identity of the commit to locate. If the object is
- * an annotated tag it will be peeled back to the commit.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_commit_lookup(
- git_commit **commit, git_repository *repo, const git_oid *id);
-
-/**
- * Lookup a commit object from a repository, given a prefix of its
- * identifier (short id).
- *
- * The returned object should be released with `git_commit_free` when no
- * longer needed.
- *
- * @see git_object_lookup_prefix
- *
- * @param commit pointer to the looked up commit
- * @param repo the repo to use when locating the commit.
- * @param id identity of the commit to locate. If the object is
- * an annotated tag it will be peeled back to the commit.
- * @param len the length of the short identifier
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_commit_lookup_prefix(
- git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
-
-/**
- * Close an open commit
- *
- * This is a wrapper around git_object_free()
- *
- * IMPORTANT:
- * It *is* necessary to call this method when you stop
- * using a commit. Failure to do so will cause a memory leak.
- *
- * @param commit the commit to close
- */
-
-GIT_EXTERN(void) git_commit_free(git_commit *commit);
-
-/**
- * Get the id of a commit.
- *
- * @param commit a previously loaded commit.
- * @return object identity for the commit.
- */
-GIT_EXTERN(const git_oid *) git_commit_id(const git_commit *commit);
-
-/**
- * Get the repository that contains the commit.
- *
- * @param commit A previously loaded commit.
- * @return Repository that contains this commit.
- */
-GIT_EXTERN(git_repository *) git_commit_owner(const git_commit *commit);
-
-/**
- * Get the encoding for the message of a commit,
- * as a string representing a standard encoding name.
- *
- * The encoding may be NULL if the `encoding` header
- * in the commit is missing; in that case UTF-8 is assumed.
- *
- * @param commit a previously loaded commit.
- * @return NULL, or the encoding
- */
-GIT_EXTERN(const char *) git_commit_message_encoding(const git_commit *commit);
-
-/**
- * Get the full message of a commit.
- *
- * The returned message will be slightly prettified by removing any
- * potential leading newlines.
- *
- * @param commit a previously loaded commit.
- * @return the message of a commit
- */
-GIT_EXTERN(const char *) git_commit_message(const git_commit *commit);
-
-/**
- * Get the full raw message of a commit.
- *
- * @param commit a previously loaded commit.
- * @return the raw message of a commit
- */
-GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit);
-
-/**
- * Get the short "summary" of the git commit message.
- *
- * The returned message is the summary of the commit, comprising the
- * first paragraph of the message with whitespace trimmed and squashed.
- *
- * @param commit a previously loaded commit.
- * @return the summary of a commit or NULL on error
- */
-GIT_EXTERN(const char *) git_commit_summary(git_commit *commit);
-
-/**
- * Get the long "body" of the git commit message.
- *
- * The returned message is the body of the commit, comprising
- * everything but the first paragraph of the message. Leading and
- * trailing whitespaces are trimmed.
- *
- * @param commit a previously loaded commit.
- * @return the body of a commit or NULL when no the message only
- * consists of a summary
- */
-GIT_EXTERN(const char *) git_commit_body(git_commit *commit);
-
-/**
- * Get the commit time (i.e. committer time) of a commit.
- *
- * @param commit a previously loaded commit.
- * @return the time of a commit
- */
-GIT_EXTERN(git_time_t) git_commit_time(const git_commit *commit);
-
-/**
- * Get the commit timezone offset (i.e. committer's preferred timezone) of a commit.
- *
- * @param commit a previously loaded commit.
- * @return positive or negative timezone offset, in minutes from UTC
- */
-GIT_EXTERN(int) git_commit_time_offset(const git_commit *commit);
-
-/**
- * Get the committer of a commit.
- *
- * @param commit a previously loaded commit.
- * @return the committer of a commit
- */
-GIT_EXTERN(const git_signature *) git_commit_committer(const git_commit *commit);
-
-/**
- * Get the author of a commit.
- *
- * @param commit a previously loaded commit.
- * @return the author of a commit
- */
-GIT_EXTERN(const git_signature *) git_commit_author(const git_commit *commit);
-
-/**
- * Get the full raw text of the commit header.
- *
- * @param commit a previously loaded commit
- * @return the header text of the commit
- */
-GIT_EXTERN(const char *) git_commit_raw_header(const git_commit *commit);
-
-/**
- * Get the tree pointed to by a commit.
- *
- * @param tree_out pointer where to store the tree object
- * @param commit a previously loaded commit.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_commit_tree(git_tree **tree_out, const git_commit *commit);
-
-/**
- * Get the id of the tree pointed to by a commit. This differs from
- * `git_commit_tree` in that no attempts are made to fetch an object
- * from the ODB.
- *
- * @param commit a previously loaded commit.
- * @return the id of tree pointed to by commit.
- */
-GIT_EXTERN(const git_oid *) git_commit_tree_id(const git_commit *commit);
-
-/**
- * Get the number of parents of this commit
- *
- * @param commit a previously loaded commit.
- * @return integer of count of parents
- */
-GIT_EXTERN(unsigned int) git_commit_parentcount(const git_commit *commit);
-
-/**
- * Get the specified parent of the commit.
- *
- * @param out Pointer where to store the parent commit
- * @param commit a previously loaded commit.
- * @param n the position of the parent (from 0 to `parentcount`)
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_commit_parent(
- git_commit **out,
- const git_commit *commit,
- unsigned int n);
-
-/**
- * Get the oid of a specified parent for a commit. This is different from
- * `git_commit_parent`, which will attempt to load the parent commit from
- * the ODB.
- *
- * @param commit a previously loaded commit.
- * @param n the position of the parent (from 0 to `parentcount`)
- * @return the id of the parent, NULL on error.
- */
-GIT_EXTERN(const git_oid *) git_commit_parent_id(
- const git_commit *commit,
- unsigned int n);
-
-/**
- * Get the commit object that is the <n>th generation ancestor
- * of the named commit object, following only the first parents.
- * The returned commit has to be freed by the caller.
- *
- * Passing `0` as the generation number returns another instance of the
- * base commit itself.
- *
- * @param ancestor Pointer where to store the ancestor commit
- * @param commit a previously loaded commit.
- * @param n the requested generation
- * @return 0 on success; GIT_ENOTFOUND if no matching ancestor exists
- * or an error code
- */
-GIT_EXTERN(int) git_commit_nth_gen_ancestor(
- git_commit **ancestor,
- const git_commit *commit,
- unsigned int n);
-
-/**
- * Get an arbitrary header field
- *
- * @param out the buffer to fill
- * @param commit the commit to look in
- * @param field the header field to return
- * @return 0 on succeess, GIT_ENOTFOUND if the field does not exist,
- * or an error code
- */
-GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, const char *field);
-
-/**
- * Extract the signature from a commit
- *
- * If the id is not for a commit, the error class will be
- * `GITERR_INVALID`. If the commit does not have a signature, the
- * error class will be `GITERR_OBJECT`.
- *
- * @param signature the signature block
- * @param signed_data signed data; this is the commit contents minus the signature block
- * @param repo the repository in which the commit exists
- * @param commit_id the commit from which to extract the data
- * @param field the name of the header field containing the signature
- * block; pass `NULL` to extract the default 'gpgsig'
- * @return 0 on success, GIT_ENOTFOUND if the id is not for a commit
- * or the commit does not have a signature.
- */
-GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field);
-
-/**
- * Create new commit in the repository from a list of `git_object` pointers
- *
- * The message will **not** be cleaned up automatically. You can do that
- * with the `git_message_prettify()` function.
- *
- * @param id Pointer in which to store the OID of the newly created commit
- *
- * @param repo Repository where to store the commit
- *
- * @param update_ref If not NULL, name of the reference that
- * will be updated to point to this commit. If the reference
- * is not direct, it will be resolved to a direct reference.
- * Use "HEAD" to update the HEAD of the current branch and
- * make it point to this commit. If the reference doesn't
- * exist yet, it will be created. If it does exist, the first
- * parent must be the tip of this branch.
- *
- * @param author Signature with author and author time of commit
- *
- * @param committer Signature with committer and * commit time of commit
- *
- * @param message_encoding The encoding for the message in the
- * commit, represented with a standard encoding name.
- * E.g. "UTF-8". If NULL, no encoding header is written and
- * UTF-8 is assumed.
- *
- * @param message Full message for this commit
- *
- * @param tree An instance of a `git_tree` object that will
- * be used as the tree for the commit. This tree object must
- * also be owned by the given `repo`.
- *
- * @param parent_count Number of parents for this commit
- *
- * @param parents Array of `parent_count` pointers to `git_commit`
- * objects that will be used as the parents for this commit. This
- * array may be NULL if `parent_count` is 0 (root commit). All the
- * given commits must be owned by the `repo`.
- *
- * @return 0 or an error code
- * The created commit will be written to the Object Database and
- * the given reference will be updated to point to it
- */
-GIT_EXTERN(int) git_commit_create(
- git_oid *id,
- git_repository *repo,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_tree *tree,
- size_t parent_count,
- const git_commit *parents[]);
-
-/**
- * Create new commit in the repository using a variable argument list.
- *
- * The message will **not** be cleaned up automatically. You can do that
- * with the `git_message_prettify()` function.
- *
- * The parents for the commit are specified as a variable list of pointers
- * to `const git_commit *`. Note that this is a convenience method which may
- * not be safe to export for certain languages or compilers
- *
- * All other parameters remain the same as `git_commit_create()`.
- *
- * @see git_commit_create
- */
-GIT_EXTERN(int) git_commit_create_v(
- git_oid *id,
- git_repository *repo,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_tree *tree,
- size_t parent_count,
- ...);
-
-/**
- * Amend an existing commit by replacing only non-NULL values.
- *
- * This creates a new commit that is exactly the same as the old commit,
- * except that any non-NULL values will be updated. The new commit has
- * the same parents as the old commit.
- *
- * The `update_ref` value works as in the regular `git_commit_create()`,
- * updating the ref to point to the newly rewritten commit. If you want
- * to amend a commit that is not currently the tip of the branch and then
- * rewrite the following commits to reach a ref, pass this as NULL and
- * update the rest of the commit chain and ref separately.
- *
- * Unlike `git_commit_create()`, the `author`, `committer`, `message`,
- * `message_encoding`, and `tree` parameters can be NULL in which case this
- * will use the values from the original `commit_to_amend`.
- *
- * All parameters have the same meanings as in `git_commit_create()`.
- *
- * @see git_commit_create
- */
-GIT_EXTERN(int) git_commit_amend(
- git_oid *id,
- const git_commit *commit_to_amend,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_tree *tree);
-
-/**
- * Create a commit and write it into a buffer
- *
- * Create a commit as with `git_commit_create()` but instead of
- * writing it to the objectdb, write the contents of the object into a
- * buffer.
- *
- * @param out the buffer into which to write the commit object content
- *
- * @param repo Repository where the referenced tree and parents live
- *
- * @param author Signature with author and author time of commit
- *
- * @param committer Signature with committer and * commit time of commit
- *
- * @param message_encoding The encoding for the message in the
- * commit, represented with a standard encoding name.
- * E.g. "UTF-8". If NULL, no encoding header is written and
- * UTF-8 is assumed.
- *
- * @param message Full message for this commit
- *
- * @param tree An instance of a `git_tree` object that will
- * be used as the tree for the commit. This tree object must
- * also be owned by the given `repo`.
- *
- * @param parent_count Number of parents for this commit
- *
- * @param parents Array of `parent_count` pointers to `git_commit`
- * objects that will be used as the parents for this commit. This
- * array may be NULL if `parent_count` is 0 (root commit). All the
- * given commits must be owned by the `repo`.
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_commit_create_buffer(
- git_buf *out,
- git_repository *repo,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_tree *tree,
- size_t parent_count,
- const git_commit *parents[]);
-
-/**
- * Create a commit object from the given buffer and signature
- *
- * Given the unsigned commit object's contents, its signature and the
- * header field in which to store the signature, attach the signature
- * to the commit and write it into the given repository.
- *
- * @param out the resulting commit id
- * @param commit_content the content of the unsigned commit object
- * @param signature the signature to add to the commit
- * @param signature_field which header field should contain this
- * signature. Leave `NULL` for the default of "gpgsig"
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_commit_create_with_signature(
- git_oid *out,
- git_repository *repo,
- const char *commit_content,
- const char *signature,
- const char *signature_field);
-
-/**
- * Create an in-memory copy of a commit. The copy must be explicitly
- * free'd or it will leak.
- *
- * @param out Pointer to store the copy of the commit
- * @param source Original commit to copy
- */
-GIT_EXTERN(int) git_commit_dup(git_commit **out, git_commit *source);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_common_h__
-#define INCLUDE_git_common_h__
-
-#include <time.h>
-#include <stdlib.h>
-
-#ifdef __cplusplus
-# define GIT_BEGIN_DECL extern "C" {
-# define GIT_END_DECL }
-#else
- /** Start declarations in C mode */
-# define GIT_BEGIN_DECL /* empty */
- /** End declarations in C mode */
-# define GIT_END_DECL /* empty */
-#endif
-
-#if defined(_MSC_VER) && _MSC_VER < 1800
- GIT_BEGIN_DECL
-# include "inttypes.h"
- GIT_END_DECL
-/** This check is needed for importing this file in an iOS/OS X framework throws an error in Xcode otherwise.*/
-#elif !defined(__CLANG_INTTYPES_H)
-# include <inttypes.h>
-#endif
-
-#ifdef DOCURIUM
-/*
- * This is so clang's doc parser acknowledges comments on functions
- * with size_t parameters.
- */
-typedef size_t size_t;
-#endif
-
-/** Declare a public function exported for application use. */
-#if __GNUC__ >= 4
-# define GIT_EXTERN(type) extern \
- __attribute__((visibility("default"))) \
- type
-#elif defined(_MSC_VER)
-# define GIT_EXTERN(type) __declspec(dllexport) type
-#else
-# define GIT_EXTERN(type) extern type
-#endif
-
-/** Declare a function's takes printf style arguments. */
-#ifdef __GNUC__
-# define GIT_FORMAT_PRINTF(a,b) __attribute__((format (printf, a, b)))
-#else
-# define GIT_FORMAT_PRINTF(a,b) /* empty */
-#endif
-
-#if (defined(_WIN32)) && !defined(__CYGWIN__)
-#define GIT_WIN32 1
-#endif
-
-#ifdef __amigaos4__
-#include <netinet/in.h>
-#endif
-
-/**
- * @file git2/common.h
- * @brief Git common platform definitions
- * @defgroup git_common Git common platform definitions
- * @ingroup Git
- * @{
- */
-
-GIT_BEGIN_DECL
-
-/**
- * The separator used in path list strings (ie like in the PATH
- * environment variable). A semi-colon ";" is used on Windows, and
- * a colon ":" for all other systems.
- */
-#ifdef GIT_WIN32
-#define GIT_PATH_LIST_SEPARATOR ';'
-#else
-#define GIT_PATH_LIST_SEPARATOR ':'
-#endif
-
-/**
- * The maximum length of a valid git path.
- */
-#define GIT_PATH_MAX 4096
-
-/**
- * The string representation of the null object ID.
- */
-#define GIT_OID_HEX_ZERO "0000000000000000000000000000000000000000"
-
-/**
- * Return the version of the libgit2 library
- * being currently used.
- *
- * @param major Store the major version number
- * @param minor Store the minor version number
- * @param rev Store the revision (patch) number
- */
-GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev);
-
-/**
- * Combinations of these values describe the features with which libgit2
- * was compiled
- */
-typedef enum {
- /**
- * If set, libgit2 was built thread-aware and can be safely used from multiple
- * threads.
- */
- GIT_FEATURE_THREADS = (1 << 0),
- /**
- * If set, libgit2 was built with and linked against a TLS implementation.
- * Custom TLS streams may still be added by the user to support HTTPS
- * regardless of this.
- */
- GIT_FEATURE_HTTPS = (1 << 1),
- /**
- * If set, libgit2 was built with and linked against libssh2. A custom
- * transport may still be added by the user to support libssh2 regardless of
- * this.
- */
- GIT_FEATURE_SSH = (1 << 2),
- /**
- * If set, libgit2 was built with support for sub-second resolution in file
- * modification times.
- */
- GIT_FEATURE_NSEC = (1 << 3),
-} git_feature_t;
-
-/**
- * Query compile time options for libgit2.
- *
- * @return A combination of GIT_FEATURE_* values.
- *
- * - GIT_FEATURE_THREADS
- * Libgit2 was compiled with thread support. Note that thread support is
- * still to be seen as a 'work in progress' - basic object lookups are
- * believed to be threadsafe, but other operations may not be.
- *
- * - GIT_FEATURE_HTTPS
- * Libgit2 supports the https:// protocol. This requires the openssl
- * library to be found when compiling libgit2.
- *
- * - GIT_FEATURE_SSH
- * Libgit2 supports the SSH protocol for network operations. This requires
- * the libssh2 library to be found when compiling libgit2
- */
-GIT_EXTERN(int) git_libgit2_features(void);
-
-/**
- * Global library options
- *
- * These are used to select which global option to set or get and are
- * used in `git_libgit2_opts()`.
- */
-typedef enum {
- GIT_OPT_GET_MWINDOW_SIZE,
- GIT_OPT_SET_MWINDOW_SIZE,
- GIT_OPT_GET_MWINDOW_MAPPED_LIMIT,
- GIT_OPT_SET_MWINDOW_MAPPED_LIMIT,
- GIT_OPT_GET_SEARCH_PATH,
- GIT_OPT_SET_SEARCH_PATH,
- GIT_OPT_SET_CACHE_OBJECT_LIMIT,
- GIT_OPT_SET_CACHE_MAX_SIZE,
- GIT_OPT_ENABLE_CACHING,
- GIT_OPT_GET_CACHED_MEMORY,
- GIT_OPT_GET_TEMPLATE_PATH,
- GIT_OPT_SET_TEMPLATE_PATH,
- GIT_OPT_SET_SSL_CERT_LOCATIONS,
- GIT_OPT_SET_USER_AGENT,
- GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
- GIT_OPT_SET_SSL_CIPHERS,
- GIT_OPT_GET_USER_AGENT,
-} git_libgit2_opt_t;
-
-/**
- * Set or query a library global option
- *
- * Available options:
- *
- * * opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *):
- *
- * > Get the maximum mmap window size
- *
- * * opts(GIT_OPT_SET_MWINDOW_SIZE, size_t):
- *
- * > Set the maximum mmap window size
- *
- * * opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t *):
- *
- * > Get the maximum memory that will be mapped in total by the library
- *
- * * opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t):
- *
- * >Set the maximum amount of memory that can be mapped at any time
- * by the library
- *
- * * opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf)
- *
- * > Get the search path for a given level of config data. "level" must
- * > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`,
- * > `GIT_CONFIG_LEVEL_XDG`, or `GIT_CONFIG_LEVEL_PROGRAMDATA`.
- * > The search path is written to the `out` buffer.
- *
- * * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
- *
- * > Set the search path for a level of config data. The search path
- * > applied to shared attributes and ignore files, too.
- * >
- * > - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR.
- * > Pass NULL to reset to the default (generally based on environment
- * > variables). Use magic path `$PATH` to include the old value
- * > of the path (if you want to prepend or append, for instance).
- * >
- * > - `level` must be `GIT_CONFIG_LEVEL_SYSTEM`,
- * > `GIT_CONFIG_LEVEL_GLOBAL`, `GIT_CONFIG_LEVEL_XDG`, or
- * > `GIT_CONFIG_LEVEL_PROGRAMDATA`.
- *
- * * opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, git_otype type, size_t size)
- *
- * > Set the maximum data size for the given type of object to be
- * > considered eligible for caching in memory. Setting to value to
- * > zero means that that type of object will not be cached.
- * > Defaults to 0 for GIT_OBJ_BLOB (i.e. won't cache blobs) and 4k
- * > for GIT_OBJ_COMMIT, GIT_OBJ_TREE, and GIT_OBJ_TAG.
- *
- * * opts(GIT_OPT_SET_CACHE_MAX_SIZE, ssize_t max_storage_bytes)
- *
- * > Set the maximum total data size that will be cached in memory
- * > across all repositories before libgit2 starts evicting objects
- * > from the cache. This is a soft limit, in that the library might
- * > briefly exceed it, but will start aggressively evicting objects
- * > from cache when that happens. The default cache size is 256MB.
- *
- * * opts(GIT_OPT_ENABLE_CACHING, int enabled)
- *
- * > Enable or disable caching completely.
- * >
- * > Because caches are repository-specific, disabling the cache
- * > cannot immediately clear all cached objects, but each cache will
- * > be cleared on the next attempt to update anything in it.
- *
- * * opts(GIT_OPT_GET_CACHED_MEMORY, ssize_t *current, ssize_t *allowed)
- *
- * > Get the current bytes in cache and the maximum that would be
- * > allowed in the cache.
- *
- * * opts(GIT_OPT_GET_TEMPLATE_PATH, git_buf *out)
- *
- * > Get the default template path.
- * > The path is written to the `out` buffer.
- *
- * * opts(GIT_OPT_SET_TEMPLATE_PATH, const char *path)
- *
- * > Set the default template path.
- * >
- * > - `path` directory of template.
- *
- * * opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, const char *file, const char *path)
- *
- * > Set the SSL certificate-authority locations.
- * >
- * > - `file` is the location of a file containing several
- * > certificates concatenated together.
- * > - `path` is the location of a directory holding several
- * > certificates, one per file.
- * >
- * > Either parameter may be `NULL`, but not both.
- *
- * * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent)
- *
- * > Set the value of the User-Agent header. This value will be
- * > appended to "git/1.0", for compatibility with other git clients.
- * >
- * > - `user_agent` is the value that will be delivered as the
- * > User-Agent header on HTTP requests.
- *
- * * opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled)
- *
- * > Enable strict input validation when creating new objects
- * > to ensure that all inputs to the new objects are valid. For
- * > example, when this is enabled, the parent(s) and tree inputs
- * > will be validated when creating a new commit. This defaults
- * > to enabled.
- *
- * * opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers)
- *
- * > Set the SSL ciphers use for HTTPS connections.
- * >
- * > - `ciphers` is the list of ciphers that are eanbled.
- *
- * @param option Option key
- * @param ... value to set the option
- * @return 0 on success, <0 on failure
- */
-GIT_EXTERN(int) git_libgit2_opts(int option, ...);
-
-/** @} */
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_config_h__
-#define INCLUDE_git_config_h__
-
-#include "common.h"
-#include "types.h"
-#include "buffer.h"
-
-/**
- * @file git2/config.h
- * @brief Git config management routines
- * @defgroup git_config Git config management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Priority level of a config file.
- * These priority levels correspond to the natural escalation logic
- * (from higher to lower) when searching for config entries in git.git.
- *
- * git_config_open_default() and git_repository_config() honor those
- * priority levels as well.
- */
-typedef enum {
- /** System-wide on Windows, for compatibility with portable git */
- GIT_CONFIG_LEVEL_PROGRAMDATA = 1,
-
- /** System-wide configuration file; /etc/gitconfig on Linux systems */
- GIT_CONFIG_LEVEL_SYSTEM = 2,
-
- /** XDG compatible configuration file; typically ~/.config/git/config */
- GIT_CONFIG_LEVEL_XDG = 3,
-
- /** User-specific configuration file (also called Global configuration
- * file); typically ~/.gitconfig
- */
- GIT_CONFIG_LEVEL_GLOBAL = 4,
-
- /** Repository specific configuration file; $WORK_DIR/.git/config on
- * non-bare repos
- */
- GIT_CONFIG_LEVEL_LOCAL = 5,
-
- /** Application specific configuration file; freely defined by applications
- */
- GIT_CONFIG_LEVEL_APP = 6,
-
- /** Represents the highest level available config file (i.e. the most
- * specific config file available that actually is loaded)
- */
- GIT_CONFIG_HIGHEST_LEVEL = -1,
-} git_config_level_t;
-
-/**
- * An entry in a configuration file
- */
-typedef struct git_config_entry {
- const char *name; /**< Name of the entry (normalised) */
- const char *value; /**< String value of the entry */
- git_config_level_t level; /**< Which config file this was found in */
- void (*free)(struct git_config_entry *entry); /**< Free function for this entry */
- void *payload; /**< Opaque value for the free function. Do not read or write */
-} git_config_entry;
-
-/**
- * Free a config entry
- */
-GIT_EXTERN(void) git_config_entry_free(git_config_entry *);
-
-typedef int (*git_config_foreach_cb)(const git_config_entry *, void *);
-typedef struct git_config_iterator git_config_iterator;
-
-/**
- * Config var type
- */
-typedef enum {
- GIT_CVAR_FALSE = 0,
- GIT_CVAR_TRUE = 1,
- GIT_CVAR_INT32,
- GIT_CVAR_STRING
-} git_cvar_t;
-
-/**
- * Mapping from config variables to values.
- */
-typedef struct {
- git_cvar_t cvar_type;
- const char *str_match;
- int map_value;
-} git_cvar_map;
-
-/**
- * Locate the path to the global configuration file
- *
- * The user or global configuration file is usually
- * located in `$HOME/.gitconfig`.
- *
- * This method will try to guess the full path to that
- * file, if the file exists. The returned path
- * may be used on any `git_config` call to load the
- * global configuration file.
- *
- * This method will not guess the path to the xdg compatible
- * config file (.config/git/config).
- *
- * @param out Pointer to a user-allocated git_buf in which to store the path
- * @return 0 if a global configuration file has been found. Its path will be stored in `out`.
- */
-GIT_EXTERN(int) git_config_find_global(git_buf *out);
-
-/**
- * Locate the path to the global xdg compatible configuration file
- *
- * The xdg compatible configuration file is usually
- * located in `$HOME/.config/git/config`.
- *
- * This method will try to guess the full path to that
- * file, if the file exists. The returned path
- * may be used on any `git_config` call to load the
- * xdg compatible configuration file.
- *
- * @param out Pointer to a user-allocated git_buf in which to store the path
- * @return 0 if a xdg compatible configuration file has been
- * found. Its path will be stored in `out`.
- */
-GIT_EXTERN(int) git_config_find_xdg(git_buf *out);
-
-/**
- * Locate the path to the system configuration file
- *
- * If /etc/gitconfig doesn't exist, it will look for
- * %PROGRAMFILES%\Git\etc\gitconfig.
- *
- * @param out Pointer to a user-allocated git_buf in which to store the path
- * @return 0 if a system configuration file has been
- * found. Its path will be stored in `out`.
- */
-GIT_EXTERN(int) git_config_find_system(git_buf *out);
-
-/**
- * Locate the path to the configuration file in ProgramData
- *
- * Look for the file in %PROGRAMDATA%\Git\config used by portable git.
- *
- * @param out Pointer to a user-allocated git_buf in which to store the path
- * @return 0 if a ProgramData configuration file has been
- * found. Its path will be stored in `out`.
- */
-GIT_EXTERN(int) git_config_find_programdata(git_buf *out);
-
-/**
- * Open the global, XDG and system configuration files
- *
- * Utility wrapper that finds the global, XDG and system configuration files
- * and opens them into a single prioritized config object that can be
- * used when accessing default config data outside a repository.
- *
- * @param out Pointer to store the config instance
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_open_default(git_config **out);
-
-/**
- * Allocate a new configuration object
- *
- * This object is empty, so you have to add a file to it before you
- * can do anything with it.
- *
- * @param out pointer to the new configuration
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_new(git_config **out);
-
-/**
- * Add an on-disk config file instance to an existing config
- *
- * The on-disk file pointed at by `path` will be opened and
- * parsed; it's expected to be a native Git config file following
- * the default Git config syntax (see man git-config).
- *
- * If the file does not exist, the file will still be added and it
- * will be created the first time we write to it.
- *
- * Note that the configuration object will free the file
- * automatically.
- *
- * Further queries on this config object will access each
- * of the config file instances in order (instances with
- * a higher priority level will be accessed first).
- *
- * @param cfg the configuration to add the file to
- * @param path path to the configuration file to add
- * @param level the priority level of the backend
- * @param force replace config file at the given priority level
- * @return 0 on success, GIT_EEXISTS when adding more than one file
- * for a given priority level (and force_replace set to 0),
- * GIT_ENOTFOUND when the file doesn't exist or error code
- */
-GIT_EXTERN(int) git_config_add_file_ondisk(
- git_config *cfg,
- const char *path,
- git_config_level_t level,
- int force);
-
-/**
- * Create a new config instance containing a single on-disk file
- *
- * This method is a simple utility wrapper for the following sequence
- * of calls:
- * - git_config_new
- * - git_config_add_file_ondisk
- *
- * @param out The configuration instance to create
- * @param path Path to the on-disk file to open
- * @return 0 on success, or an error code
- */
-GIT_EXTERN(int) git_config_open_ondisk(git_config **out, const char *path);
-
-/**
- * Build a single-level focused config object from a multi-level one.
- *
- * The returned config object can be used to perform get/set/delete operations
- * on a single specific level.
- *
- * Getting several times the same level from the same parent multi-level config
- * will return different config instances, but containing the same config_file
- * instance.
- *
- * @param out The configuration instance to create
- * @param parent Multi-level config to search for the given level
- * @param level Configuration level to search for
- * @return 0, GIT_ENOTFOUND if the passed level cannot be found in the
- * multi-level parent config, or an error code
- */
-GIT_EXTERN(int) git_config_open_level(
- git_config **out,
- const git_config *parent,
- git_config_level_t level);
-
-/**
- * Open the global/XDG configuration file according to git's rules
- *
- * Git allows you to store your global configuration at
- * `$HOME/.config` or `$XDG_CONFIG_HOME/git/config`. For backwards
- * compatability, the XDG file shouldn't be used unless the use has
- * created it explicitly. With this function you'll open the correct
- * one to write to.
- *
- * @param out pointer in which to store the config object
- * @param config the config object in which to look
- */
-GIT_EXTERN(int) git_config_open_global(git_config **out, git_config *config);
-
-/**
- * Create a snapshot of the configuration
- *
- * Create a snapshot of the current state of a configuration, which
- * allows you to look into a consistent view of the configuration for
- * looking up complex values (e.g. a remote, submodule).
- *
- * The string returned when querying such a config object is valid
- * until it is freed.
- *
- * @param out pointer in which to store the snapshot config object
- * @param config configuration to snapshot
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_snapshot(git_config **out, git_config *config);
-
-/**
- * Free the configuration and its associated memory and files
- *
- * @param cfg the configuration to free
- */
-GIT_EXTERN(void) git_config_free(git_config *cfg);
-
-/**
- * Get the git_config_entry of a config variable.
- *
- * Free the git_config_entry after use with `git_config_entry_free()`.
- *
- * @param out pointer to the variable git_config_entry
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_get_entry(
- git_config_entry **out,
- const git_config *cfg,
- const char *name);
-
-/**
- * Get the value of an integer config variable.
- *
- * All config files will be looked into, in the order of their
- * defined level. A higher level means a higher priority. The
- * first occurrence of the variable will be returned here.
- *
- * @param out pointer to the variable where the value should be stored
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_get_int32(int32_t *out, const git_config *cfg, const char *name);
-
-/**
- * Get the value of a long integer config variable.
- *
- * All config files will be looked into, in the order of their
- * defined level. A higher level means a higher priority. The
- * first occurrence of the variable will be returned here.
- *
- * @param out pointer to the variable where the value should be stored
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_get_int64(int64_t *out, const git_config *cfg, const char *name);
-
-/**
- * Get the value of a boolean config variable.
- *
- * This function uses the usual C convention of 0 being false and
- * anything else true.
- *
- * All config files will be looked into, in the order of their
- * defined level. A higher level means a higher priority. The
- * first occurrence of the variable will be returned here.
- *
- * @param out pointer to the variable where the value should be stored
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_get_bool(int *out, const git_config *cfg, const char *name);
-
-/**
- * Get the value of a path config variable.
- *
- * A leading '~' will be expanded to the global search path (which
- * defaults to the user's home directory but can be overridden via
- * `git_libgit2_opts()`.
- *
- * All config files will be looked into, in the order of their
- * defined level. A higher level means a higher priority. The
- * first occurrence of the variable will be returned here.
- *
- * @param out the buffer in which to store the result
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_get_path(git_buf *out, const git_config *cfg, const char *name);
-
-/**
- * Get the value of a string config variable.
- *
- * This function can only be used on snapshot config objects. The
- * string is owned by the config and should not be freed by the
- * user. The pointer will be valid until the config is freed.
- *
- * All config files will be looked into, in the order of their
- * defined level. A higher level means a higher priority. The
- * first occurrence of the variable will be returned here.
- *
- * @param out pointer to the string
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, const char *name);
-
-/**
- * Get the value of a string config variable.
- *
- * The value of the config will be copied into the buffer.
- *
- * All config files will be looked into, in the order of their
- * defined level. A higher level means a higher priority. The
- * first occurrence of the variable will be returned here.
- *
- * @param out buffer in which to store the string
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_get_string_buf(git_buf *out, const git_config *cfg, const char *name);
-
-/**
- * Get each value of a multivar in a foreach callback
- *
- * The callback will be called on each variable found
- *
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @param regexp regular expression to filter which variables we're
- * interested in. Use NULL to indicate all
- * @param callback the function to be called on each value of the variable
- * @param payload opaque pointer to pass to the callback
- */
-GIT_EXTERN(int) git_config_get_multivar_foreach(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
-
-/**
- * Get each value of a multivar
- *
- * @param out pointer to store the iterator
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @param regexp regular expression to filter which variables we're
- * interested in. Use NULL to indicate all
- */
-GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp);
-
-/**
- * Return the current entry and advance the iterator
- *
- * The pointers returned by this function are valid until the iterator
- * is freed.
- *
- * @param entry pointer to store the entry
- * @param iter the iterator
- * @return 0 or an error code. GIT_ITEROVER if the iteration has completed
- */
-GIT_EXTERN(int) git_config_next(git_config_entry **entry, git_config_iterator *iter);
-
-/**
- * Free a config iterator
- *
- * @param iter the iterator to free
- */
-GIT_EXTERN(void) git_config_iterator_free(git_config_iterator *iter);
-
-/**
- * Set the value of an integer config variable in the config file
- * with the highest level (usually the local one).
- *
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @param value Integer value for the variable
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_set_int32(git_config *cfg, const char *name, int32_t value);
-
-/**
- * Set the value of a long integer config variable in the config file
- * with the highest level (usually the local one).
- *
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @param value Long integer value for the variable
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_set_int64(git_config *cfg, const char *name, int64_t value);
-
-/**
- * Set the value of a boolean config variable in the config file
- * with the highest level (usually the local one).
- *
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @param value the value to store
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value);
-
-/**
- * Set the value of a string config variable in the config file
- * with the highest level (usually the local one).
- *
- * A copy of the string is made and the user is free to use it
- * afterwards.
- *
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @param value the string to store.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value);
-
-/**
- * Set a multivar in the local config file.
- *
- * @param cfg where to look for the variable
- * @param name the variable's name
- * @param regexp a regular expression to indicate which values to replace
- * @param value the new value.
- */
-GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value);
-
-/**
- * Delete a config variable from the config file
- * with the highest level (usually the local one).
- *
- * @param cfg the configuration
- * @param name the variable to delete
- */
-GIT_EXTERN(int) git_config_delete_entry(git_config *cfg, const char *name);
-
-/**
- * Deletes one or several entries from a multivar in the local config file.
- *
- * @param cfg where to look for the variables
- * @param name the variable's name
- * @param regexp a regular expression to indicate which values to delete
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp);
-
-/**
- * Perform an operation on each config variable.
- *
- * The callback receives the normalized name and value of each variable
- * in the config backend, and the data pointer passed to this function.
- * If the callback returns a non-zero value, the function stops iterating
- * and returns that value to the caller.
- *
- * The pointers passed to the callback are only valid as long as the
- * iteration is ongoing.
- *
- * @param cfg where to get the variables from
- * @param callback the function to call on each variable
- * @param payload the data to pass to the callback
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_config_foreach(
- const git_config *cfg,
- git_config_foreach_cb callback,
- void *payload);
-
-/**
- * Iterate over all the config variables
- *
- * Use `git_config_next` to advance the iteration and
- * `git_config_iterator_free` when done.
- *
- * @param out pointer to store the iterator
- * @param cfg where to ge the variables from
- */
-GIT_EXTERN(int) git_config_iterator_new(git_config_iterator **out, const git_config *cfg);
-
-/**
- * Iterate over all the config variables whose name matches a pattern
- *
- * Use `git_config_next` to advance the iteration and
- * `git_config_iterator_free` when done.
- *
- * @param out pointer to store the iterator
- * @param cfg where to ge the variables from
- * @param regexp regular expression to match the names
- */
-GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp);
-
-/**
- * Perform an operation on each config variable matching a regular expression.
- *
- * This behaviors like `git_config_foreach` with an additional filter of a
- * regular expression that filters which config keys are passed to the
- * callback.
- *
- * The pointers passed to the callback are only valid as long as the
- * iteration is ongoing.
- *
- * @param cfg where to get the variables from
- * @param regexp regular expression to match against config names
- * @param callback the function to call on each variable
- * @param payload the data to pass to the callback
- * @return 0 or the return value of the callback which didn't return 0
- */
-GIT_EXTERN(int) git_config_foreach_match(
- const git_config *cfg,
- const char *regexp,
- git_config_foreach_cb callback,
- void *payload);
-
-/**
- * Query the value of a config variable and return it mapped to
- * an integer constant.
- *
- * This is a helper method to easily map different possible values
- * to a variable to integer constants that easily identify them.
- *
- * A mapping array looks as follows:
- *
- * git_cvar_map autocrlf_mapping[] = {
- * {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
- * {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
- * {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT},
- * {GIT_CVAR_STRING, "default", GIT_AUTO_CRLF_DEFAULT}};
- *
- * On any "false" value for the variable (e.g. "false", "FALSE", "no"), the
- * mapping will store `GIT_AUTO_CRLF_FALSE` in the `out` parameter.
- *
- * The same thing applies for any "true" value such as "true", "yes" or "1", storing
- * the `GIT_AUTO_CRLF_TRUE` variable.
- *
- * Otherwise, if the value matches the string "input" (with case insensitive comparison),
- * the given constant will be stored in `out`, and likewise for "default".
- *
- * If not a single match can be made to store in `out`, an error code will be
- * returned.
- *
- * @param out place to store the result of the mapping
- * @param cfg config file to get the variables from
- * @param name name of the config variable to lookup
- * @param maps array of `git_cvar_map` objects specifying the possible mappings
- * @param map_n number of mapping objects in `maps`
- * @return 0 on success, error code otherwise
- */
-GIT_EXTERN(int) git_config_get_mapped(
- int *out,
- const git_config *cfg,
- const char *name,
- const git_cvar_map *maps,
- size_t map_n);
-
-/**
- * Maps a string value to an integer constant
- *
- * @param out place to store the result of the parsing
- * @param maps array of `git_cvar_map` objects specifying the possible mappings
- * @param map_n number of mapping objects in `maps`
- * @param value value to parse
- */
-GIT_EXTERN(int) git_config_lookup_map_value(
- int *out,
- const git_cvar_map *maps,
- size_t map_n,
- const char *value);
-
-/**
- * Parse a string value as a bool.
- *
- * Valid values for true are: 'true', 'yes', 'on', 1 or any
- * number different from 0
- * Valid values for false are: 'false', 'no', 'off', 0
- *
- * @param out place to store the result of the parsing
- * @param value value to parse
- */
-GIT_EXTERN(int) git_config_parse_bool(int *out, const char *value);
-
-/**
- * Parse a string value as an int32.
- *
- * An optional value suffix of 'k', 'm', or 'g' will
- * cause the value to be multiplied by 1024, 1048576,
- * or 1073741824 prior to output.
- *
- * @param out place to store the result of the parsing
- * @param value value to parse
- */
-GIT_EXTERN(int) git_config_parse_int32(int32_t *out, const char *value);
-
-/**
- * Parse a string value as an int64.
- *
- * An optional value suffix of 'k', 'm', or 'g' will
- * cause the value to be multiplied by 1024, 1048576,
- * or 1073741824 prior to output.
- *
- * @param out place to store the result of the parsing
- * @param value value to parse
- */
-GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value);
-
-/**
- * Parse a string value as a path.
- *
- * A leading '~' will be expanded to the global search path (which
- * defaults to the user's home directory but can be overridden via
- * `git_libgit2_opts()`.
- *
- * If the value does not begin with a tilde, the input will be
- * returned.
- *
- * @param out placae to store the result of parsing
- * @param value the path to evaluate
- */
-GIT_EXTERN(int) git_config_parse_path(git_buf *out, const char *value);
-
-/**
- * Perform an operation on each config variable in given config backend
- * matching a regular expression.
- *
- * This behaviors like `git_config_foreach_match` except instead of all config
- * entries it just enumerates through the given backend entry.
- *
- * @param backend where to get the variables from
- * @param regexp regular expression to match against config names (can be NULL)
- * @param callback the function to call on each variable
- * @param payload the data to pass to the callback
- */
-GIT_EXTERN(int) git_config_backend_foreach_match(
- git_config_backend *backend,
- const char *regexp,
- git_config_foreach_cb callback,
- void *payload);
-
-
-/**
- * Lock the backend with the highest priority
- *
- * Locking disallows anybody else from writing to that backend. Any
- * updates made after locking will not be visible to a reader until
- * the file is unlocked.
- *
- * You can apply the changes by calling `git_transaction_commit()`
- * before freeing the transaction. Either of these actions will unlock
- * the config.
- *
- * @param tx the resulting transaction, use this to commit or undo the
- * changes
- * @param cfg the configuration in which to lock
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_lock(git_transaction **tx, git_config *cfg);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_cred_helpers_h__
-#define INCLUDE_git_cred_helpers_h__
-
-#include "transport.h"
-
-/**
- * @file git2/cred_helpers.h
- * @brief Utility functions for credential management
- * @defgroup git_cred_helpers credential management helpers
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Payload for git_cred_stock_userpass_plaintext.
- */
-typedef struct git_cred_userpass_payload {
- const char *username;
- const char *password;
-} git_cred_userpass_payload;
-
-
-/**
- * Stock callback usable as a git_cred_acquire_cb. This calls
- * git_cred_userpass_plaintext_new unless the protocol has not specified
- * `GIT_CREDTYPE_USERPASS_PLAINTEXT` as an allowed type.
- *
- * @param cred The newly created credential object.
- * @param url The resource for which we are demanding a credential.
- * @param user_from_url The username that was embedded in a "user\@host"
- * remote url, or NULL if not included.
- * @param allowed_types A bitmask stating which cred types are OK to return.
- * @param payload The payload provided when specifying this callback. (This is
- * interpreted as a `git_cred_userpass_payload*`.)
- */
-GIT_EXTERN(int) git_cred_userpass(
- git_cred **cred,
- const char *url,
- const char *user_from_url,
- unsigned int allowed_types,
- void *payload);
-
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_describe_h__
-#define INCLUDE_git_describe_h__
-
-#include "common.h"
-#include "types.h"
-#include "buffer.h"
-
-/**
- * @file git2/describe.h
- * @brief Git describing routines
- * @defgroup git_describe Git describing routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Reference lookup strategy
- *
- * These behave like the --tags and --all optios to git-describe,
- * namely they say to look for any reference in either refs/tags/ or
- * refs/ respectively.
- */
-typedef enum {
- GIT_DESCRIBE_DEFAULT,
- GIT_DESCRIBE_TAGS,
- GIT_DESCRIBE_ALL,
-} git_describe_strategy_t;
-
-/**
- * Describe options structure
- *
- * Initialize with `GIT_DESCRIBE_OPTIONS_INIT` macro to correctly set
- * the `version` field. E.g.
- *
- * git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
- */
-typedef struct git_describe_options {
- unsigned int version;
-
- unsigned int max_candidates_tags; /**< default: 10 */
- unsigned int describe_strategy; /**< default: GIT_DESCRIBE_DEFAULT */
- const char *pattern;
- /**
- * When calculating the distance from the matching tag or
- * reference, only walk down the first-parent ancestry.
- */
- int only_follow_first_parent;
- /**
- * If no matching tag or reference is found, the describe
- * operation would normally fail. If this option is set, it
- * will instead fall back to showing the full id of the
- * commit.
- */
- int show_commit_oid_as_fallback;
-} git_describe_options;
-
-#define GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS 10
-#define GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE 7
-
-#define GIT_DESCRIBE_OPTIONS_VERSION 1
-#define GIT_DESCRIBE_OPTIONS_INIT { \
- GIT_DESCRIBE_OPTIONS_VERSION, \
- GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS, \
-}
-
-GIT_EXTERN(int) git_describe_init_options(git_describe_options *opts, unsigned int version);
-
-/**
- * Options for formatting the describe string
- */
-typedef struct {
- unsigned int version;
-
- /**
- * Size of the abbreviated commit id to use. This value is the
- * lower bound for the length of the abbreviated string. The
- * default is 7.
- */
- unsigned int abbreviated_size;
-
- /**
- * Set to use the long format even when a shorter name could be used.
- */
- int always_use_long_format;
-
- /**
- * If the workdir is dirty and this is set, this string will
- * be appended to the description string.
- */
- const char *dirty_suffix;
-} git_describe_format_options;
-
-#define GIT_DESCRIBE_FORMAT_OPTIONS_VERSION 1
-#define GIT_DESCRIBE_FORMAT_OPTIONS_INIT { \
- GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, \
- GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE, \
- }
-
-GIT_EXTERN(int) git_describe_init_format_options(git_describe_format_options *opts, unsigned int version);
-
-/**
- * A struct that stores the result of a describe operation.
- */
-typedef struct git_describe_result git_describe_result;
-
-/**
- * Describe a commit
- *
- * Perform the describe operation on the given committish object.
- *
- * @param result pointer to store the result. You must free this once
- * you're done with it.
- * @param committish a committish to describe
- * @param opts the lookup options
- */
-GIT_EXTERN(int) git_describe_commit(
- git_describe_result **result,
- git_object *committish,
- git_describe_options *opts);
-
-/**
- * Describe a commit
- *
- * Perform the describe operation on the current commit and the
- * worktree. After peforming describe on HEAD, a status is run and the
- * description is considered to be dirty if there are.
- *
- * @param out pointer to store the result. You must free this once
- * you're done with it.
- * @param repo the repository in which to perform the describe
- * @param opts the lookup options
- */
-GIT_EXTERN(int) git_describe_workdir(
- git_describe_result **out,
- git_repository *repo,
- git_describe_options *opts);
-
-/**
- * Print the describe result to a buffer
- *
- * @param out The buffer to store the result
- * @param result the result from `git_describe_commit()` or
- * `git_describe_workdir()`.
- * @param opts the formatting options
- */
-GIT_EXTERN(int) git_describe_format(
- git_buf *out,
- const git_describe_result *result,
- const git_describe_format_options *opts);
-
-/**
- * Free the describe result.
- */
-GIT_EXTERN(void) git_describe_result_free(git_describe_result *result);
-
-/** @} */
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_diff_h__
-#define INCLUDE_git_diff_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "tree.h"
-#include "refs.h"
-
-/**
- * @file git2/diff.h
- * @brief Git tree and file differencing routines.
- *
- * Overview
- * --------
- *
- * Calculating diffs is generally done in two phases: building a list of
- * diffs then traversing it. This makes is easier to share logic across
- * the various types of diffs (tree vs tree, workdir vs index, etc.), and
- * also allows you to insert optional diff post-processing phases,
- * such as rename detection, in between the steps. When you are done with
- * a diff object, it must be freed.
- *
- * Terminology
- * -----------
- *
- * To understand the diff APIs, you should know the following terms:
- *
- * - A `diff` represents the cumulative list of differences between two
- * snapshots of a repository (possibly filtered by a set of file name
- * patterns). This is the `git_diff` object.
- *
- * - A `delta` is a file pair with an old and new revision. The old version
- * may be absent if the file was just created and the new version may be
- * absent if the file was deleted. A diff is mostly just a list of deltas.
- *
- * - A `binary` file / delta is a file (or pair) for which no text diffs
- * should be generated. A diff can contain delta entries that are
- * binary, but no diff content will be output for those files. There is
- * a base heuristic for binary detection and you can further tune the
- * behavior with git attributes or diff flags and option settings.
- *
- * - A `hunk` is a span of modified lines in a delta along with some stable
- * surrounding context. You can configure the amount of context and other
- * properties of how hunks are generated. Each hunk also comes with a
- * header that described where it starts and ends in both the old and new
- * versions in the delta.
- *
- * - A `line` is a range of characters inside a hunk. It could be a context
- * line (i.e. in both old and new versions), an added line (i.e. only in
- * the new version), or a removed line (i.e. only in the old version).
- * Unfortunately, we don't know anything about the encoding of data in the
- * file being diffed, so we cannot tell you much about the line content.
- * Line data will not be NUL-byte terminated, however, because it will be
- * just a span of bytes inside the larger file.
- *
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Flags for diff options. A combination of these flags can be passed
- * in via the `flags` value in the `git_diff_options`.
- */
-typedef enum {
- /** Normal diff, the default */
- GIT_DIFF_NORMAL = 0,
-
- /*
- * Options controlling which files will be in the diff
- */
-
- /** Reverse the sides of the diff */
- GIT_DIFF_REVERSE = (1u << 0),
-
- /** Include ignored files in the diff */
- GIT_DIFF_INCLUDE_IGNORED = (1u << 1),
-
- /** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
- * will be marked with only a single entry in the diff; this flag
- * adds all files under the directory as IGNORED entries, too.
- */
- GIT_DIFF_RECURSE_IGNORED_DIRS = (1u << 2),
-
- /** Include untracked files in the diff */
- GIT_DIFF_INCLUDE_UNTRACKED = (1u << 3),
-
- /** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked
- * directory will be marked with only a single entry in the diff
- * (a la what core Git does in `git status`); this flag adds *all*
- * files under untracked directories as UNTRACKED entries, too.
- */
- GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1u << 4),
-
- /** Include unmodified files in the diff */
- GIT_DIFF_INCLUDE_UNMODIFIED = (1u << 5),
-
- /** Normally, a type change between files will be converted into a
- * DELETED record for the old and an ADDED record for the new; this
- * options enabled the generation of TYPECHANGE delta records.
- */
- GIT_DIFF_INCLUDE_TYPECHANGE = (1u << 6),
-
- /** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
- * generally show as a DELETED blob. This flag tries to correctly
- * label blob->tree transitions as TYPECHANGE records with new_file's
- * mode set to tree. Note: the tree SHA will not be available.
- */
- GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1u << 7),
-
- /** Ignore file mode changes */
- GIT_DIFF_IGNORE_FILEMODE = (1u << 8),
-
- /** Treat all submodules as unmodified */
- GIT_DIFF_IGNORE_SUBMODULES = (1u << 9),
-
- /** Use case insensitive filename comparisons */
- GIT_DIFF_IGNORE_CASE = (1u << 10),
-
- /** May be combined with `GIT_DIFF_IGNORE_CASE` to specify that a file
- * that has changed case will be returned as an add/delete pair.
- */
- GIT_DIFF_INCLUDE_CASECHANGE = (1u << 11),
-
- /** If the pathspec is set in the diff options, this flags indicates
- * that the paths will be treated as literal paths instead of
- * fnmatch patterns. Each path in the list must either be a full
- * path to a file or a directory. (A trailing slash indicates that
- * the path will _only_ match a directory). If a directory is
- * specified, all children will be included.
- */
- GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1u << 12),
-
- /** Disable updating of the `binary` flag in delta records. This is
- * useful when iterating over a diff if you don't need hunk and data
- * callbacks and want to avoid having to load file completely.
- */
- GIT_DIFF_SKIP_BINARY_CHECK = (1u << 13),
-
- /** When diff finds an untracked directory, to match the behavior of
- * core Git, it scans the contents for IGNORED and UNTRACKED files.
- * If *all* contents are IGNORED, then the directory is IGNORED; if
- * any contents are not IGNORED, then the directory is UNTRACKED.
- * This is extra work that may not matter in many cases. This flag
- * turns off that scan and immediately labels an untracked directory
- * as UNTRACKED (changing the behavior to not match core Git).
- */
- GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS = (1u << 14),
-
- /** When diff finds a file in the working directory with stat
- * information different from the index, but the OID ends up being the
- * same, write the correct stat information into the index. Note:
- * without this flag, diff will always leave the index untouched.
- */
- GIT_DIFF_UPDATE_INDEX = (1u << 15),
-
- /** Include unreadable files in the diff */
- GIT_DIFF_INCLUDE_UNREADABLE = (1u << 16),
-
- /** Include unreadable files in the diff */
- GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 17),
-
- /*
- * Options controlling how output will be generated
- */
-
- /** Treat all files as text, disabling binary attributes & detection */
- GIT_DIFF_FORCE_TEXT = (1u << 20),
- /** Treat all files as binary, disabling text diffs */
- GIT_DIFF_FORCE_BINARY = (1u << 21),
-
- /** Ignore all whitespace */
- GIT_DIFF_IGNORE_WHITESPACE = (1u << 22),
- /** Ignore changes in amount of whitespace */
- GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1u << 23),
- /** Ignore whitespace at end of line */
- GIT_DIFF_IGNORE_WHITESPACE_EOL = (1u << 24),
-
- /** When generating patch text, include the content of untracked
- * files. This automatically turns on GIT_DIFF_INCLUDE_UNTRACKED but
- * it does not turn on GIT_DIFF_RECURSE_UNTRACKED_DIRS. Add that
- * flag if you want the content of every single UNTRACKED file.
- */
- GIT_DIFF_SHOW_UNTRACKED_CONTENT = (1u << 25),
-
- /** When generating output, include the names of unmodified files if
- * they are included in the git_diff. Normally these are skipped in
- * the formats that list files (e.g. name-only, name-status, raw).
- * Even with this, these will not be included in patch format.
- */
- GIT_DIFF_SHOW_UNMODIFIED = (1u << 26),
-
- /** Use the "patience diff" algorithm */
- GIT_DIFF_PATIENCE = (1u << 28),
- /** Take extra time to find minimal diff */
- GIT_DIFF_MINIMAL = (1 << 29),
-
- /** Include the necessary deflate / delta information so that `git-apply`
- * can apply given diff information to binary files.
- */
- GIT_DIFF_SHOW_BINARY = (1 << 30),
-} git_diff_option_t;
-
-/**
- * The diff object that contains all individual file deltas.
- *
- * This is an opaque structure which will be allocated by one of the diff
- * generator functions below (such as `git_diff_tree_to_tree`). You are
- * responsible for releasing the object memory when done, using the
- * `git_diff_free()` function.
- */
-typedef struct git_diff git_diff;
-
-/**
- * Flags for the delta object and the file objects on each side.
- *
- * These flags are used for both the `flags` value of the `git_diff_delta`
- * and the flags for the `git_diff_file` objects representing the old and
- * new sides of the delta. Values outside of this public range should be
- * considered reserved for internal or future use.
- */
-typedef enum {
- GIT_DIFF_FLAG_BINARY = (1u << 0), /**< file(s) treated as binary data */
- GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /**< file(s) treated as text data */
- GIT_DIFF_FLAG_VALID_ID = (1u << 2), /**< `id` value is known correct */
- GIT_DIFF_FLAG_EXISTS = (1u << 3), /**< file exists at this side of the delta */
-} git_diff_flag_t;
-
-/**
- * What type of change is described by a git_diff_delta?
- *
- * `GIT_DELTA_RENAMED` and `GIT_DELTA_COPIED` will only show up if you run
- * `git_diff_find_similar()` on the diff object.
- *
- * `GIT_DELTA_TYPECHANGE` only shows up given `GIT_DIFF_INCLUDE_TYPECHANGE`
- * in the option flags (otherwise type changes will be split into ADDED /
- * DELETED pairs).
- */
-typedef enum {
- GIT_DELTA_UNMODIFIED = 0, /**< no changes */
- GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */
- GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */
- GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */
- GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */
- GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */
- GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */
- GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */
- GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */
- GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */
- GIT_DELTA_CONFLICTED = 10, /**< entry in the index is conflicted */
-} git_delta_t;
-
-/**
- * Description of one side of a delta.
- *
- * Although this is called a "file", it could represent a file, a symbolic
- * link, a submodule commit id, or even a tree (although that only if you
- * are tracking type changes or ignored/untracked directories).
- *
- * The `id` is the `git_oid` of the item. If the entry represents an
- * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
- * then the oid will be zeroes.
- *
- * `path` is the NUL-terminated path to the entry relative to the working
- * directory of the repository.
- *
- * `size` is the size of the entry in bytes.
- *
- * `flags` is a combination of the `git_diff_flag_t` types
- *
- * `mode` is, roughly, the stat() `st_mode` value for the item. This will
- * be restricted to one of the `git_filemode_t` values.
- *
- * The `id_abbrev` represents the known length of the `id` field, when
- * converted to a hex string. It is generally `GIT_OID_HEXSZ`, unless this
- * delta was created from reading a patch file, in which case it may be
- * abbreviated to something reasonable, like 7 characters.
- */
-typedef struct {
- git_oid id;
- const char *path;
- git_off_t size;
- uint32_t flags;
- uint16_t mode;
- uint16_t id_abbrev;
-} git_diff_file;
-
-/**
- * Description of changes to one entry.
- *
- * When iterating over a diff, this will be passed to most callbacks and
- * you can use the contents to understand exactly what has changed.
- *
- * The `old_file` represents the "from" side of the diff and the `new_file`
- * represents to "to" side of the diff. What those means depend on the
- * function that was used to generate the diff and will be documented below.
- * You can also use the `GIT_DIFF_REVERSE` flag to flip it around.
- *
- * Although the two sides of the delta are named "old_file" and "new_file",
- * they actually may correspond to entries that represent a file, a symbolic
- * link, a submodule commit id, or even a tree (if you are tracking type
- * changes or ignored/untracked directories).
- *
- * Under some circumstances, in the name of efficiency, not all fields will
- * be filled in, but we generally try to fill in as much as possible. One
- * example is that the "flags" field may not have either the `BINARY` or the
- * `NOT_BINARY` flag set to avoid examining file contents if you do not pass
- * in hunk and/or line callbacks to the diff foreach iteration function. It
- * will just use the git attributes for those files.
- *
- * The similarity score is zero unless you call `git_diff_find_similar()`
- * which does a similarity analysis of files in the diff. Use that
- * function to do rename and copy detection, and to split heavily modified
- * files in add/delete pairs. After that call, deltas with a status of
- * GIT_DELTA_RENAMED or GIT_DELTA_COPIED will have a similarity score
- * between 0 and 100 indicating how similar the old and new sides are.
- *
- * If you ask `git_diff_find_similar` to find heavily modified files to
- * break, but to not *actually* break the records, then GIT_DELTA_MODIFIED
- * records may have a non-zero similarity score if the self-similarity is
- * below the split threshold. To display this value like core Git, invert
- * the score (a la `printf("M%03d", 100 - delta->similarity)`).
- */
-typedef struct {
- git_delta_t status;
- uint32_t flags; /**< git_diff_flag_t values */
- uint16_t similarity; /**< for RENAMED and COPIED, value 0-100 */
- uint16_t nfiles; /**< number of files in this delta */
- git_diff_file old_file;
- git_diff_file new_file;
-} git_diff_delta;
-
-/**
- * Diff notification callback function.
- *
- * The callback will be called for each file, just before the `git_delta_t`
- * gets inserted into the diff.
- *
- * When the callback:
- * - returns < 0, the diff process will be aborted.
- * - returns > 0, the delta will not be inserted into the diff, but the
- * diff process continues.
- * - returns 0, the delta is inserted into the diff, and the diff process
- * continues.
- */
-typedef int (*git_diff_notify_cb)(
- const git_diff *diff_so_far,
- const git_diff_delta *delta_to_add,
- const char *matched_pathspec,
- void *payload);
-
-/**
- * Diff progress callback.
- *
- * Called before each file comparison.
- *
- * @param diff_so_far The diff being generated.
- * @param old_path The path to the old file or NULL.
- * @param new_path The path to the new file or NULL.
- * @return Non-zero to abort the diff.
- */
-typedef int (*git_diff_progress_cb)(
- const git_diff *diff_so_far,
- const char *old_path,
- const char *new_path,
- void *payload);
-
-/**
- * Structure describing options about how the diff should be executed.
- *
- * Setting all values of the structure to zero will yield the default
- * values. Similarly, passing NULL for the options structure will
- * give the defaults. The default values are marked below.
- *
- * - `flags` is a combination of the `git_diff_option_t` values above
- * - `context_lines` is the number of unchanged lines that define the
- * boundary of a hunk (and to display before and after)
- * - `interhunk_lines` is the maximum number of unchanged lines between
- * hunk boundaries before the hunks will be merged into a one.
- * - `old_prefix` is the virtual "directory" to prefix to old file names
- * in hunk headers (default "a")
- * - `new_prefix` is the virtual "directory" to prefix to new file names
- * in hunk headers (default "b")
- * - `pathspec` is an array of paths / fnmatch patterns to constrain diff
- * - `max_size` is a file size (in bytes) above which a blob will be marked
- * as binary automatically; pass a negative value to disable.
- * - `notify_cb` is an optional callback function, notifying the consumer of
- * changes to the diff as new deltas are added.
- * - `progress_cb` is an optional callback function, notifying the consumer of
- * which files are being examined as the diff is generated.
- * - `payload` is the payload to pass to the callback functions.
- * - `ignore_submodules` overrides the submodule ignore setting for all
- * submodules in the diff.
- */
-typedef struct {
- unsigned int version; /**< version for the struct */
- uint32_t flags; /**< defaults to GIT_DIFF_NORMAL */
-
- /* options controlling which files are in the diff */
-
- git_submodule_ignore_t ignore_submodules; /**< submodule ignore rule */
- git_strarray pathspec; /**< defaults to include all paths */
- git_diff_notify_cb notify_cb;
- git_diff_progress_cb progress_cb;
- void *payload;
-
- /* options controlling how to diff text is generated */
-
- uint32_t context_lines; /**< defaults to 3 */
- uint32_t interhunk_lines; /**< defaults to 0 */
- uint16_t id_abbrev; /**< default 'core.abbrev' or 7 if unset */
- git_off_t max_size; /**< defaults to 512MB */
- const char *old_prefix; /**< defaults to "a" */
- const char *new_prefix; /**< defaults to "b" */
-} git_diff_options;
-
-/* The current version of the diff options structure */
-#define GIT_DIFF_OPTIONS_VERSION 1
-
-/* Stack initializer for diff options. Alternatively use
- * `git_diff_options_init` programmatic initialization.
- */
-#define GIT_DIFF_OPTIONS_INIT \
- {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3}
-
-/**
- * Initializes a `git_diff_options` with default values. Equivalent to
- * creating an instance with GIT_DIFF_OPTIONS_INIT.
- *
- * @param opts The `git_diff_options` struct to initialize
- * @param version Version of struct; pass `GIT_DIFF_OPTIONS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_diff_init_options(
- git_diff_options *opts,
- unsigned int version);
-
-/**
- * When iterating over a diff, callback that will be made per file.
- *
- * @param delta A pointer to the delta data for the file
- * @param progress Goes from 0 to 1 over the diff
- * @param payload User-specified pointer from foreach function
- */
-typedef int (*git_diff_file_cb)(
- const git_diff_delta *delta,
- float progress,
- void *payload);
-
-#define GIT_DIFF_HUNK_HEADER_SIZE 128
-
-/**
- * When producing a binary diff, the binary data returned will be
- * either the deflated full ("literal") contents of the file, or
- * the deflated binary delta between the two sides (whichever is
- * smaller).
- */
-typedef enum {
- /** There is no binary delta. */
- GIT_DIFF_BINARY_NONE,
-
- /** The binary data is the literal contents of the file. */
- GIT_DIFF_BINARY_LITERAL,
-
- /** The binary data is the delta from one side to the other. */
- GIT_DIFF_BINARY_DELTA,
-} git_diff_binary_t;
-
-/** The contents of one of the files in a binary diff. */
-typedef struct {
- /** The type of binary data for this file. */
- git_diff_binary_t type;
-
- /** The binary data, deflated. */
- const char *data;
-
- /** The length of the binary data. */
- size_t datalen;
-
- /** The length of the binary data after inflation. */
- size_t inflatedlen;
-} git_diff_binary_file;
-
-/** Structure describing the binary contents of a diff. */
-typedef struct {
- /**
- * Whether there is data in this binary structure or not. If this
- * is `1`, then this was produced and included binary content. If
- * this is `0` then this was generated knowing only that a binary
- * file changed but without providing the data, probably from a patch
- * that said `Binary files a/file.txt and b/file.txt differ`.
- */
- unsigned int contains_data;
- git_diff_binary_file old_file; /**< The contents of the old file. */
- git_diff_binary_file new_file; /**< The contents of the new file. */
-} git_diff_binary;
-
-/**
-* When iterating over a diff, callback that will be made for
-* binary content within the diff.
-*/
-typedef int(*git_diff_binary_cb)(
- const git_diff_delta *delta,
- const git_diff_binary *binary,
- void *payload);
-
-/**
- * Structure describing a hunk of a diff.
- */
-typedef struct {
- int old_start; /** Starting line number in old_file */
- int old_lines; /** Number of lines in old_file */
- int new_start; /** Starting line number in new_file */
- int new_lines; /** Number of lines in new_file */
- size_t header_len; /** Number of bytes in header text */
- char header[GIT_DIFF_HUNK_HEADER_SIZE]; /** Header text, NUL-byte terminated */
-} git_diff_hunk;
-
-/**
- * When iterating over a diff, callback that will be made per hunk.
- */
-typedef int (*git_diff_hunk_cb)(
- const git_diff_delta *delta,
- const git_diff_hunk *hunk,
- void *payload);
-
-/**
- * Line origin constants.
- *
- * These values describe where a line came from and will be passed to
- * the git_diff_line_cb when iterating over a diff. There are some
- * special origin constants at the end that are used for the text
- * output callbacks to demarcate lines that are actually part of
- * the file or hunk headers.
- */
-typedef enum {
- /* These values will be sent to `git_diff_line_cb` along with the line */
- GIT_DIFF_LINE_CONTEXT = ' ',
- GIT_DIFF_LINE_ADDITION = '+',
- GIT_DIFF_LINE_DELETION = '-',
-
- GIT_DIFF_LINE_CONTEXT_EOFNL = '=', /**< Both files have no LF at end */
- GIT_DIFF_LINE_ADD_EOFNL = '>', /**< Old has no LF at end, new does */
- GIT_DIFF_LINE_DEL_EOFNL = '<', /**< Old has LF at end, new does not */
-
- /* The following values will only be sent to a `git_diff_line_cb` when
- * the content of a diff is being formatted through `git_diff_print`.
- */
- GIT_DIFF_LINE_FILE_HDR = 'F',
- GIT_DIFF_LINE_HUNK_HDR = 'H',
- GIT_DIFF_LINE_BINARY = 'B' /**< For "Binary files x and y differ" */
-} git_diff_line_t;
-
-/**
- * Structure describing a line (or data span) of a diff.
- */
-typedef struct {
- char origin; /**< A git_diff_line_t value */
- int old_lineno; /**< Line number in old file or -1 for added line */
- int new_lineno; /**< Line number in new file or -1 for deleted line */
- int num_lines; /**< Number of newline characters in content */
- size_t content_len; /**< Number of bytes of data */
- git_off_t content_offset; /**< Offset in the original file to the content */
- const char *content; /**< Pointer to diff text, not NUL-byte terminated */
-} git_diff_line;
-
-/**
- * When iterating over a diff, callback that will be made per text diff
- * line. In this context, the provided range will be NULL.
- *
- * When printing a diff, callback that will be made to output each line
- * of text. This uses some extra GIT_DIFF_LINE_... constants for output
- * of lines of file and hunk headers.
- */
-typedef int (*git_diff_line_cb)(
- const git_diff_delta *delta, /**< delta that contains this data */
- const git_diff_hunk *hunk, /**< hunk containing this data */
- const git_diff_line *line, /**< line data */
- void *payload); /**< user reference data */
-
-/**
- * Flags to control the behavior of diff rename/copy detection.
- */
-typedef enum {
- /** Obey `diff.renames`. Overridden by any other GIT_DIFF_FIND_... flag. */
- GIT_DIFF_FIND_BY_CONFIG = 0,
-
- /** Look for renames? (`--find-renames`) */
- GIT_DIFF_FIND_RENAMES = (1u << 0),
-
- /** Consider old side of MODIFIED for renames? (`--break-rewrites=N`) */
- GIT_DIFF_FIND_RENAMES_FROM_REWRITES = (1u << 1),
-
- /** Look for copies? (a la `--find-copies`). */
- GIT_DIFF_FIND_COPIES = (1u << 2),
-
- /** Consider UNMODIFIED as copy sources? (`--find-copies-harder`).
- *
- * For this to work correctly, use GIT_DIFF_INCLUDE_UNMODIFIED when
- * the initial `git_diff` is being generated.
- */
- GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1u << 3),
-
- /** Mark significant rewrites for split (`--break-rewrites=/M`) */
- GIT_DIFF_FIND_REWRITES = (1u << 4),
- /** Actually split large rewrites into delete/add pairs */
- GIT_DIFF_BREAK_REWRITES = (1u << 5),
- /** Mark rewrites for split and break into delete/add pairs */
- GIT_DIFF_FIND_AND_BREAK_REWRITES =
- (GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES),
-
- /** Find renames/copies for UNTRACKED items in working directory.
- *
- * For this to work correctly, use GIT_DIFF_INCLUDE_UNTRACKED when the
- * initial `git_diff` is being generated (and obviously the diff must
- * be against the working directory for this to make sense).
- */
- GIT_DIFF_FIND_FOR_UNTRACKED = (1u << 6),
-
- /** Turn on all finding features. */
- GIT_DIFF_FIND_ALL = (0x0ff),
-
- /** Measure similarity ignoring leading whitespace (default) */
- GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0,
- /** Measure similarity ignoring all whitespace */
- GIT_DIFF_FIND_IGNORE_WHITESPACE = (1u << 12),
- /** Measure similarity including all data */
- GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1u << 13),
- /** Measure similarity only by comparing SHAs (fast and cheap) */
- GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1u << 14),
-
- /** Do not break rewrites unless they contribute to a rename.
- *
- * Normally, GIT_DIFF_FIND_AND_BREAK_REWRITES will measure the self-
- * similarity of modified files and split the ones that have changed a
- * lot into a DELETE / ADD pair. Then the sides of that pair will be
- * considered candidates for rename and copy detection.
- *
- * If you add this flag in and the split pair is *not* used for an
- * actual rename or copy, then the modified record will be restored to
- * a regular MODIFIED record instead of being split.
- */
- GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY = (1u << 15),
-
- /** Remove any UNMODIFIED deltas after find_similar is done.
- *
- * Using GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED to emulate the
- * --find-copies-harder behavior requires building a diff with the
- * GIT_DIFF_INCLUDE_UNMODIFIED flag. If you do not want UNMODIFIED
- * records in the final result, pass this flag to have them removed.
- */
- GIT_DIFF_FIND_REMOVE_UNMODIFIED = (1u << 16),
-} git_diff_find_t;
-
-/**
- * Pluggable similarity metric
- */
-typedef struct {
- int (*file_signature)(
- void **out, const git_diff_file *file,
- const char *fullpath, void *payload);
- int (*buffer_signature)(
- void **out, const git_diff_file *file,
- const char *buf, size_t buflen, void *payload);
- void (*free_signature)(void *sig, void *payload);
- int (*similarity)(int *score, void *siga, void *sigb, void *payload);
- void *payload;
-} git_diff_similarity_metric;
-
-/**
- * Control behavior of rename and copy detection
- *
- * These options mostly mimic parameters that can be passed to git-diff.
- *
- * - `rename_threshold` is the same as the -M option with a value
- * - `copy_threshold` is the same as the -C option with a value
- * - `rename_from_rewrite_threshold` matches the top of the -B option
- * - `break_rewrite_threshold` matches the bottom of the -B option
- * - `rename_limit` is the maximum number of matches to consider for
- * a particular file. This is a little different from the `-l` option
- * to regular Git because we will still process up to this many matches
- * before abandoning the search.
- *
- * The `metric` option allows you to plug in a custom similarity metric.
- * Set it to NULL for the default internal metric which is based on sampling
- * hashes of ranges of data in the file. The default metric is a pretty
- * good similarity approximation that should work fairly well for both text
- * and binary data, and is pretty fast with fixed memory overhead.
- */
-typedef struct {
- unsigned int version;
-
- /**
- * Combination of git_diff_find_t values (default GIT_DIFF_FIND_BY_CONFIG).
- * NOTE: if you don't explicitly set this, `diff.renames` could be set
- * to false, resulting in `git_diff_find_similar` doing nothing.
- */
- uint32_t flags;
-
- /** Similarity to consider a file renamed (default 50) */
- uint16_t rename_threshold;
- /** Similarity of modified to be eligible rename source (default 50) */
- uint16_t rename_from_rewrite_threshold;
- /** Similarity to consider a file a copy (default 50) */
- uint16_t copy_threshold;
- /** Similarity to split modify into delete/add pair (default 60) */
- uint16_t break_rewrite_threshold;
-
- /** Maximum similarity sources to examine for a file (somewhat like
- * git-diff's `-l` option or `diff.renameLimit` config) (default 200)
- */
- size_t rename_limit;
-
- /** Pluggable similarity metric; pass NULL to use internal metric */
- git_diff_similarity_metric *metric;
-} git_diff_find_options;
-
-#define GIT_DIFF_FIND_OPTIONS_VERSION 1
-#define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION}
-
-/**
- * Initializes a `git_diff_find_options` with default values. Equivalent to
- * creating an instance with GIT_DIFF_FIND_OPTIONS_INIT.
- *
- * @param opts The `git_diff_find_options` struct to initialize
- * @param version Version of struct; pass `GIT_DIFF_FIND_OPTIONS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_diff_find_init_options(
- git_diff_find_options *opts,
- unsigned int version);
-
-/** @name Diff Generator Functions
- *
- * These are the functions you would use to create (or destroy) a
- * git_diff from various objects in a repository.
- */
-/**@{*/
-
-/**
- * Deallocate a diff.
- *
- * @param diff The previously created diff; cannot be used after free.
- */
-GIT_EXTERN(void) git_diff_free(git_diff *diff);
-
-/**
- * Create a diff with the difference between two tree objects.
- *
- * This is equivalent to `git diff <old-tree> <new-tree>`
- *
- * The first tree will be used for the "old_file" side of the delta and the
- * second tree will be used for the "new_file" side of the delta. You can
- * pass NULL to indicate an empty tree, although it is an error to pass
- * NULL for both the `old_tree` and `new_tree`.
- *
- * @param diff Output pointer to a git_diff pointer to be allocated.
- * @param repo The repository containing the trees.
- * @param old_tree A git_tree object to diff from, or NULL for empty tree.
- * @param new_tree A git_tree object to diff to, or NULL for empty tree.
- * @param opts Structure with options to influence diff or NULL for defaults.
- */
-GIT_EXTERN(int) git_diff_tree_to_tree(
- git_diff **diff,
- git_repository *repo,
- git_tree *old_tree,
- git_tree *new_tree,
- const git_diff_options *opts); /**< can be NULL for defaults */
-
-/**
- * Create a diff between a tree and repository index.
- *
- * This is equivalent to `git diff --cached <treeish>` or if you pass
- * the HEAD tree, then like `git diff --cached`.
- *
- * The tree you pass will be used for the "old_file" side of the delta, and
- * the index will be used for the "new_file" side of the delta.
- *
- * If you pass NULL for the index, then the existing index of the `repo`
- * will be used. In this case, the index will be refreshed from disk
- * (if it has changed) before the diff is generated.
- *
- * @param diff Output pointer to a git_diff pointer to be allocated.
- * @param repo The repository containing the tree and index.
- * @param old_tree A git_tree object to diff from, or NULL for empty tree.
- * @param index The index to diff with; repo index used if NULL.
- * @param opts Structure with options to influence diff or NULL for defaults.
- */
-GIT_EXTERN(int) git_diff_tree_to_index(
- git_diff **diff,
- git_repository *repo,
- git_tree *old_tree,
- git_index *index,
- const git_diff_options *opts); /**< can be NULL for defaults */
-
-/**
- * Create a diff between the repository index and the workdir directory.
- *
- * This matches the `git diff` command. See the note below on
- * `git_diff_tree_to_workdir` for a discussion of the difference between
- * `git diff` and `git diff HEAD` and how to emulate a `git diff <treeish>`
- * using libgit2.
- *
- * The index will be used for the "old_file" side of the delta, and the
- * working directory will be used for the "new_file" side of the delta.
- *
- * If you pass NULL for the index, then the existing index of the `repo`
- * will be used. In this case, the index will be refreshed from disk
- * (if it has changed) before the diff is generated.
- *
- * @param diff Output pointer to a git_diff pointer to be allocated.
- * @param repo The repository.
- * @param index The index to diff from; repo index used if NULL.
- * @param opts Structure with options to influence diff or NULL for defaults.
- */
-GIT_EXTERN(int) git_diff_index_to_workdir(
- git_diff **diff,
- git_repository *repo,
- git_index *index,
- const git_diff_options *opts); /**< can be NULL for defaults */
-
-/**
- * Create a diff between a tree and the working directory.
- *
- * The tree you provide will be used for the "old_file" side of the delta,
- * and the working directory will be used for the "new_file" side.
- *
- * This is not the same as `git diff <treeish>` or `git diff-index
- * <treeish>`. Those commands use information from the index, whereas this
- * function strictly returns the differences between the tree and the files
- * in the working directory, regardless of the state of the index. Use
- * `git_diff_tree_to_workdir_with_index` to emulate those commands.
- *
- * To see difference between this and `git_diff_tree_to_workdir_with_index`,
- * consider the example of a staged file deletion where the file has then
- * been put back into the working dir and further modified. The
- * tree-to-workdir diff for that file is 'modified', but `git diff` would
- * show status 'deleted' since there is a staged delete.
- *
- * @param diff A pointer to a git_diff pointer that will be allocated.
- * @param repo The repository containing the tree.
- * @param old_tree A git_tree object to diff from, or NULL for empty tree.
- * @param opts Structure with options to influence diff or NULL for defaults.
- */
-GIT_EXTERN(int) git_diff_tree_to_workdir(
- git_diff **diff,
- git_repository *repo,
- git_tree *old_tree,
- const git_diff_options *opts); /**< can be NULL for defaults */
-
-/**
- * Create a diff between a tree and the working directory using index data
- * to account for staged deletes, tracked files, etc.
- *
- * This emulates `git diff <tree>` by diffing the tree to the index and
- * the index to the working directory and blending the results into a
- * single diff that includes staged deleted, etc.
- *
- * @param diff A pointer to a git_diff pointer that will be allocated.
- * @param repo The repository containing the tree.
- * @param old_tree A git_tree object to diff from, or NULL for empty tree.
- * @param opts Structure with options to influence diff or NULL for defaults.
- */
-GIT_EXTERN(int) git_diff_tree_to_workdir_with_index(
- git_diff **diff,
- git_repository *repo,
- git_tree *old_tree,
- const git_diff_options *opts); /**< can be NULL for defaults */
-
-/**
- * Create a diff with the difference between two index objects.
- *
- * The first index will be used for the "old_file" side of the delta and the
- * second index will be used for the "new_file" side of the delta.
- *
- * @param diff Output pointer to a git_diff pointer to be allocated.
- * @param repo The repository containing the indexes.
- * @param old_index A git_index object to diff from.
- * @param new_index A git_index object to diff to.
- * @param opts Structure with options to influence diff or NULL for defaults.
- */
-GIT_EXTERN(int) git_diff_index_to_index(
- git_diff **diff,
- git_repository *repo,
- git_index *old_index,
- git_index *new_index,
- const git_diff_options *opts); /**< can be NULL for defaults */
-
-/**
- * Merge one diff into another.
- *
- * This merges items from the "from" list into the "onto" list. The
- * resulting diff will have all items that appear in either list.
- * If an item appears in both lists, then it will be "merged" to appear
- * as if the old version was from the "onto" list and the new version
- * is from the "from" list (with the exception that if the item has a
- * pending DELETE in the middle, then it will show as deleted).
- *
- * @param onto Diff to merge into.
- * @param from Diff to merge.
- */
-GIT_EXTERN(int) git_diff_merge(
- git_diff *onto,
- const git_diff *from);
-
-/**
- * Transform a diff marking file renames, copies, etc.
- *
- * This modifies a diff in place, replacing old entries that look
- * like renames or copies with new entries reflecting those changes.
- * This also will, if requested, break modified files into add/remove
- * pairs if the amount of change is above a threshold.
- *
- * @param diff diff to run detection algorithms on
- * @param options Control how detection should be run, NULL for defaults
- * @return 0 on success, -1 on failure
- */
-GIT_EXTERN(int) git_diff_find_similar(
- git_diff *diff,
- const git_diff_find_options *options);
-
-/**@}*/
-
-
-/** @name Diff Processor Functions
- *
- * These are the functions you apply to a diff to process it
- * or read it in some way.
- */
-/**@{*/
-
-/**
- * Query how many diff records are there in a diff.
- *
- * @param diff A git_diff generated by one of the above functions
- * @return Count of number of deltas in the list
- */
-GIT_EXTERN(size_t) git_diff_num_deltas(const git_diff *diff);
-
-/**
- * Query how many diff deltas are there in a diff filtered by type.
- *
- * This works just like `git_diff_entrycount()` with an extra parameter
- * that is a `git_delta_t` and returns just the count of how many deltas
- * match that particular type.
- *
- * @param diff A git_diff generated by one of the above functions
- * @param type A git_delta_t value to filter the count
- * @return Count of number of deltas matching delta_t type
- */
-GIT_EXTERN(size_t) git_diff_num_deltas_of_type(
- const git_diff *diff, git_delta_t type);
-
-/**
- * Return the diff delta for an entry in the diff list.
- *
- * The `git_diff_delta` pointer points to internal data and you do not
- * have to release it when you are done with it. It will go away when
- * the * `git_diff` (or any associated `git_patch`) goes away.
- *
- * Note that the flags on the delta related to whether it has binary
- * content or not may not be set if there are no attributes set for the
- * file and there has been no reason to load the file data at this point.
- * For now, if you need those flags to be up to date, your only option is
- * to either use `git_diff_foreach` or create a `git_patch`.
- *
- * @param diff Diff list object
- * @param idx Index into diff list
- * @return Pointer to git_diff_delta (or NULL if `idx` out of range)
- */
-GIT_EXTERN(const git_diff_delta *) git_diff_get_delta(
- const git_diff *diff, size_t idx);
-
-/**
- * Check if deltas are sorted case sensitively or insensitively.
- *
- * @param diff diff to check
- * @return 0 if case sensitive, 1 if case is ignored
- */
-GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff);
-
-/**
- * Loop over all deltas in a diff issuing callbacks.
- *
- * This will iterate through all of the files described in a diff. You
- * should provide a file callback to learn about each file.
- *
- * The "hunk" and "line" callbacks are optional, and the text diff of the
- * files will only be calculated if they are not NULL. Of course, these
- * callbacks will not be invoked for binary files on the diff or for
- * files whose only changed is a file mode change.
- *
- * Returning a non-zero value from any of the callbacks will terminate
- * the iteration and return the value to the user.
- *
- * @param diff A git_diff generated by one of the above functions.
- * @param file_cb Callback function to make per file in the diff.
- * @param binary_cb Optional callback to make for binary files.
- * @param hunk_cb Optional callback to make per hunk of text diff. This
- * callback is called to describe a range of lines in the
- * diff. It will not be issued for binary files.
- * @param line_cb Optional callback to make per line of diff text. This
- * same callback will be made for context lines, added, and
- * removed lines, and even for a deleted trailing newline.
- * @param payload Reference pointer that will be passed to your callbacks.
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_diff_foreach(
- git_diff *diff,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb line_cb,
- void *payload);
-
-/**
- * Look up the single character abbreviation for a delta status code.
- *
- * When you run `git diff --name-status` it uses single letter codes in
- * the output such as 'A' for added, 'D' for deleted, 'M' for modified,
- * etc. This function converts a git_delta_t value into these letters for
- * your own purposes. GIT_DELTA_UNTRACKED will return a space (i.e. ' ').
- *
- * @param status The git_delta_t value to look up
- * @return The single character label for that code
- */
-GIT_EXTERN(char) git_diff_status_char(git_delta_t status);
-
-/**
- * Possible output formats for diff data
- */
-typedef enum {
- GIT_DIFF_FORMAT_PATCH = 1u, /**< full git diff */
- GIT_DIFF_FORMAT_PATCH_HEADER = 2u, /**< just the file headers of patch */
- GIT_DIFF_FORMAT_RAW = 3u, /**< like git diff --raw */
- GIT_DIFF_FORMAT_NAME_ONLY = 4u, /**< like git diff --name-only */
- GIT_DIFF_FORMAT_NAME_STATUS = 5u, /**< like git diff --name-status */
-} git_diff_format_t;
-
-/**
- * Iterate over a diff generating formatted text output.
- *
- * Returning a non-zero value from the callbacks will terminate the
- * iteration and return the non-zero value to the caller.
- *
- * @param diff A git_diff generated by one of the above functions.
- * @param format A git_diff_format_t value to pick the text format.
- * @param print_cb Callback to make per line of diff text.
- * @param payload Reference pointer that will be passed to your callback.
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_diff_print(
- git_diff *diff,
- git_diff_format_t format,
- git_diff_line_cb print_cb,
- void *payload);
-
-/**
- * Produce the complete formatted text output from a diff into a
- * buffer.
- *
- * @param out A pointer to a user-allocated git_buf that will
- * contain the diff text
- * @param diff A git_diff generated by one of the above functions.
- * @param format A git_diff_format_t value to pick the text format.
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_diff_to_buf(
- git_buf *out,
- git_diff *diff,
- git_diff_format_t format);
-
-/**@}*/
-
-
-/*
- * Misc
- */
-
-/**
- * Directly run a diff on two blobs.
- *
- * Compared to a file, a blob lacks some contextual information. As such,
- * the `git_diff_file` given to the callback will have some fake data; i.e.
- * `mode` will be 0 and `path` will be NULL.
- *
- * NULL is allowed for either `old_blob` or `new_blob` and will be treated
- * as an empty blob, with the `oid` set to NULL in the `git_diff_file` data.
- * Passing NULL for both blobs is a noop; no callbacks will be made at all.
- *
- * We do run a binary content check on the blob content and if either blob
- * looks like binary data, the `git_diff_delta` binary attribute will be set
- * to 1 and no call to the hunk_cb nor line_cb will be made (unless you pass
- * `GIT_DIFF_FORCE_TEXT` of course).
- *
- * @param old_blob Blob for old side of diff, or NULL for empty blob
- * @param old_as_path Treat old blob as if it had this filename; can be NULL
- * @param new_blob Blob for new side of diff, or NULL for empty blob
- * @param new_as_path Treat new blob as if it had this filename; can be NULL
- * @param options Options for diff, or NULL for default options
- * @param file_cb Callback for "file"; made once if there is a diff; can be NULL
- * @param binary_cb Callback for binary files; can be NULL
- * @param hunk_cb Callback for each hunk in diff; can be NULL
- * @param line_cb Callback for each line in diff; can be NULL
- * @param payload Payload passed to each callback function
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_diff_blobs(
- const git_blob *old_blob,
- const char *old_as_path,
- const git_blob *new_blob,
- const char *new_as_path,
- const git_diff_options *options,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb line_cb,
- void *payload);
-
-/**
- * Directly run a diff between a blob and a buffer.
- *
- * As with `git_diff_blobs`, comparing a blob and buffer lacks some context,
- * so the `git_diff_file` parameters to the callbacks will be faked a la the
- * rules for `git_diff_blobs()`.
- *
- * Passing NULL for `old_blob` will be treated as an empty blob (i.e. the
- * `file_cb` will be invoked with GIT_DELTA_ADDED and the diff will be the
- * entire content of the buffer added). Passing NULL to the buffer will do
- * the reverse, with GIT_DELTA_REMOVED and blob content removed.
- *
- * @param old_blob Blob for old side of diff, or NULL for empty blob
- * @param old_as_path Treat old blob as if it had this filename; can be NULL
- * @param buffer Raw data for new side of diff, or NULL for empty
- * @param buffer_len Length of raw data for new side of diff
- * @param buffer_as_path Treat buffer as if it had this filename; can be NULL
- * @param options Options for diff, or NULL for default options
- * @param file_cb Callback for "file"; made once if there is a diff; can be NULL
- * @param binary_cb Callback for binary files; can be NULL
- * @param hunk_cb Callback for each hunk in diff; can be NULL
- * @param line_cb Callback for each line in diff; can be NULL
- * @param payload Payload passed to each callback function
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_diff_blob_to_buffer(
- const git_blob *old_blob,
- const char *old_as_path,
- const char *buffer,
- size_t buffer_len,
- const char *buffer_as_path,
- const git_diff_options *options,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb line_cb,
- void *payload);
-
-/**
- * Directly run a diff between two buffers.
- *
- * Even more than with `git_diff_blobs`, comparing two buffer lacks
- * context, so the `git_diff_file` parameters to the callbacks will be
- * faked a la the rules for `git_diff_blobs()`.
- *
- * @param old_buffer Raw data for old side of diff, or NULL for empty
- * @param old_len Length of the raw data for old side of the diff
- * @param old_as_path Treat old buffer as if it had this filename; can be NULL
- * @param new_buffer Raw data for new side of diff, or NULL for empty
- * @param new_len Length of raw data for new side of diff
- * @param new_as_path Treat buffer as if it had this filename; can be NULL
- * @param options Options for diff, or NULL for default options
- * @param file_cb Callback for "file"; made once if there is a diff; can be NULL
- * @param binary_cb Callback for binary files; can be NULL
- * @param hunk_cb Callback for each hunk in diff; can be NULL
- * @param line_cb Callback for each line in diff; can be NULL
- * @param payload Payload passed to each callback function
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_diff_buffers(
- const void *old_buffer,
- size_t old_len,
- const char *old_as_path,
- const void *new_buffer,
- size_t new_len,
- const char *new_as_path,
- const git_diff_options *options,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb line_cb,
- void *payload);
-
-/**
- * Read the contents of a git patch file into a `git_diff` object.
- *
- * The diff object produced is similar to the one that would be
- * produced if you actually produced it computationally by comparing
- * two trees, however there may be subtle differences. For example,
- * a patch file likely contains abbreviated object IDs, so the
- * object IDs in a `git_diff_delta` produced by this function will
- * also be abbreviated.
- *
- * This function will only read patch files created by a git
- * implementation, it will not read unified diffs produced by
- * the `diff` program, nor any other types of patch files.
- *
- * @param out A pointer to a git_diff pointer that will be allocated.
- * @param content The contents of a patch file
- * @param content_len The length of the patch file contents
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_diff_from_buffer(
- git_diff **out,
- const char *content,
- size_t content_len);
-
-/**
- * This is an opaque structure which is allocated by `git_diff_get_stats`.
- * You are responsible for releasing the object memory when done, using the
- * `git_diff_stats_free()` function.
- */
-typedef struct git_diff_stats git_diff_stats;
-
-/**
- * Formatting options for diff stats
- */
-typedef enum {
- /** No stats*/
- GIT_DIFF_STATS_NONE = 0,
-
- /** Full statistics, equivalent of `--stat` */
- GIT_DIFF_STATS_FULL = (1u << 0),
-
- /** Short statistics, equivalent of `--shortstat` */
- GIT_DIFF_STATS_SHORT = (1u << 1),
-
- /** Number statistics, equivalent of `--numstat` */
- GIT_DIFF_STATS_NUMBER = (1u << 2),
-
- /** Extended header information such as creations, renames and mode changes, equivalent of `--summary` */
- GIT_DIFF_STATS_INCLUDE_SUMMARY = (1u << 3),
-} git_diff_stats_format_t;
-
-/**
- * Accumulate diff statistics for all patches.
- *
- * @param out Structure containg the diff statistics.
- * @param diff A git_diff generated by one of the above functions.
- * @return 0 on success; non-zero on error
- */
-GIT_EXTERN(int) git_diff_get_stats(
- git_diff_stats **out,
- git_diff *diff);
-
-/**
- * Get the total number of files changed in a diff
- *
- * @param stats A `git_diff_stats` generated by one of the above functions.
- * @return total number of files changed in the diff
- */
-GIT_EXTERN(size_t) git_diff_stats_files_changed(
- const git_diff_stats *stats);
-
-/**
- * Get the total number of insertions in a diff
- *
- * @param stats A `git_diff_stats` generated by one of the above functions.
- * @return total number of insertions in the diff
- */
-GIT_EXTERN(size_t) git_diff_stats_insertions(
- const git_diff_stats *stats);
-
-/**
- * Get the total number of deletions in a diff
- *
- * @param stats A `git_diff_stats` generated by one of the above functions.
- * @return total number of deletions in the diff
- */
-GIT_EXTERN(size_t) git_diff_stats_deletions(
- const git_diff_stats *stats);
-
-/**
- * Print diff statistics to a `git_buf`.
- *
- * @param out buffer to store the formatted diff statistics in.
- * @param stats A `git_diff_stats` generated by one of the above functions.
- * @param format Formatting option.
- * @param width Target width for output (only affects GIT_DIFF_STATS_FULL)
- * @return 0 on success; non-zero on error
- */
-GIT_EXTERN(int) git_diff_stats_to_buf(
- git_buf *out,
- const git_diff_stats *stats,
- git_diff_stats_format_t format,
- size_t width);
-
-/**
- * Deallocate a `git_diff_stats`.
- *
- * @param stats The previously created statistics object;
- * cannot be used after free.
- */
-GIT_EXTERN(void) git_diff_stats_free(git_diff_stats *stats);
-
-/**
- * Formatting options for diff e-mail generation
- */
-typedef enum {
- /** Normal patch, the default */
- GIT_DIFF_FORMAT_EMAIL_NONE = 0,
-
- /** Don't insert "[PATCH]" in the subject header*/
- GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0),
-
-} git_diff_format_email_flags_t;
-
-/**
- * Options for controlling the formatting of the generated e-mail.
- */
-typedef struct {
- unsigned int version;
-
- git_diff_format_email_flags_t flags;
-
- /** This patch number */
- size_t patch_no;
-
- /** Total number of patches in this series */
- size_t total_patches;
-
- /** id to use for the commit */
- const git_oid *id;
-
- /** Summary of the change */
- const char *summary;
-
- /** Commit message's body */
- const char *body;
-
- /** Author of the change */
- const git_signature *author;
-} git_diff_format_email_options;
-
-#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1
-#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL, NULL}
-
-/**
- * Create an e-mail ready patch from a diff.
- *
- * @param out buffer to store the e-mail patch in
- * @param diff containing the commit
- * @param opts structure with options to influence content and formatting.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_diff_format_email(
- git_buf *out,
- git_diff *diff,
- const git_diff_format_email_options *opts);
-
-/**
- * Create an e-mail ready patch for a commit.
- *
- * Does not support creating patches for merge commits (yet).
- *
- * @param out buffer to store the e-mail patch in
- * @param repo containing the commit
- * @param commit pointer to up commit
- * @param patch_no patch number of the commit
- * @param total_patches total number of patches in the patch set
- * @param flags determines the formatting of the e-mail
- * @param diff_opts structure with options to influence diff or NULL for defaults.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_diff_commit_as_email(
- git_buf *out,
- git_repository *repo,
- git_commit *commit,
- size_t patch_no,
- size_t total_patches,
- git_diff_format_email_flags_t flags,
- const git_diff_options *diff_opts);
-
-/**
- * Initializes a `git_diff_format_email_options` with default values.
- *
- * Equivalent to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
- *
- * @param opts The `git_diff_format_email_options` struct to initialize
- * @param version Version of struct; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_diff_format_email_init_options(
- git_diff_format_email_options *opts,
- unsigned int version);
-
-GIT_END_DECL
-
-/** @} */
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_errors_h__
-#define INCLUDE_git_errors_h__
-
-#include "common.h"
-
-/**
- * @file git2/errors.h
- * @brief Git error handling routines and variables
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/** Generic return codes */
-typedef enum {
- GIT_OK = 0, /**< No error */
-
- GIT_ERROR = -1, /**< Generic error */
- GIT_ENOTFOUND = -3, /**< Requested object could not be found */
- GIT_EEXISTS = -4, /**< Object exists preventing operation */
- GIT_EAMBIGUOUS = -5, /**< More than one object matches */
- GIT_EBUFS = -6, /**< Output buffer too short to hold data */
-
- /* GIT_EUSER is a special error that is never generated by libgit2
- * code. You can return it from a callback (e.g to stop an iteration)
- * to know that it was generated by the callback and not by libgit2.
- */
- GIT_EUSER = -7,
-
- GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */
- GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */
- GIT_EUNMERGED = -10, /**< Merge in progress prevented operation */
- GIT_ENONFASTFORWARD = -11, /**< Reference was not fast-forwardable */
- GIT_EINVALIDSPEC = -12, /**< Name/ref spec was not in a valid format */
- GIT_ECONFLICT = -13, /**< Checkout conflicts prevented operation */
- GIT_ELOCKED = -14, /**< Lock file prevented operation */
- GIT_EMODIFIED = -15, /**< Reference value does not match expected */
- GIT_EAUTH = -16, /**< Authentication error */
- GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */
- GIT_EAPPLIED = -18, /**< Patch/merge has already been applied */
- GIT_EPEEL = -19, /**< The requested peel operation is not possible */
- GIT_EEOF = -20, /**< Unexpected EOF */
- GIT_EINVALID = -21, /**< Invalid operation or input */
- GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */
- GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */
- GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */
-
- GIT_PASSTHROUGH = -30, /**< Internal only */
- GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
-} git_error_code;
-
-/**
- * Structure to store extra details of the last error that occurred.
- *
- * This is kept on a per-thread basis if GIT_THREADS was defined when the
- * library was build, otherwise one is kept globally for the library
- */
-typedef struct {
- char *message;
- int klass;
-} git_error;
-
-/** Error classes */
-typedef enum {
- GITERR_NONE = 0,
- GITERR_NOMEMORY,
- GITERR_OS,
- GITERR_INVALID,
- GITERR_REFERENCE,
- GITERR_ZLIB,
- GITERR_REPOSITORY,
- GITERR_CONFIG,
- GITERR_REGEX,
- GITERR_ODB,
- GITERR_INDEX,
- GITERR_OBJECT,
- GITERR_NET,
- GITERR_TAG,
- GITERR_TREE,
- GITERR_INDEXER,
- GITERR_SSL,
- GITERR_SUBMODULE,
- GITERR_THREAD,
- GITERR_STASH,
- GITERR_CHECKOUT,
- GITERR_FETCHHEAD,
- GITERR_MERGE,
- GITERR_SSH,
- GITERR_FILTER,
- GITERR_REVERT,
- GITERR_CALLBACK,
- GITERR_CHERRYPICK,
- GITERR_DESCRIBE,
- GITERR_REBASE,
- GITERR_FILESYSTEM,
- GITERR_PATCH,
-} git_error_t;
-
-/**
- * Return the last `git_error` object that was generated for the
- * current thread or NULL if no error has occurred.
- *
- * @return A git_error object.
- */
-GIT_EXTERN(const git_error *) giterr_last(void);
-
-/**
- * Clear the last library error that occurred for this thread.
- */
-GIT_EXTERN(void) giterr_clear(void);
-
-/**
- * Set the error message string for this thread.
- *
- * This function is public so that custom ODB backends and the like can
- * relay an error message through libgit2. Most regular users of libgit2
- * will never need to call this function -- actually, calling it in most
- * circumstances (for example, calling from within a callback function)
- * will just end up having the value overwritten by libgit2 internals.
- *
- * This error message is stored in thread-local storage and only applies
- * to the particular thread that this libgit2 call is made from.
- *
- * @param error_class One of the `git_error_t` enum above describing the
- * general subsystem that is responsible for the error.
- * @param string The formatted error message to keep
- */
-GIT_EXTERN(void) giterr_set_str(int error_class, const char *string);
-
-/**
- * Set the error message to a special value for memory allocation failure.
- *
- * The normal `giterr_set_str()` function attempts to `strdup()` the string
- * that is passed in. This is not a good idea when the error in question
- * is a memory allocation failure. That circumstance has a special setter
- * function that sets the error string to a known and statically allocated
- * internal value.
- */
-GIT_EXTERN(void) giterr_set_oom(void);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_filter_h__
-#define INCLUDE_git_filter_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "buffer.h"
-
-/**
- * @file git2/filter.h
- * @brief Git filter APIs
- *
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Filters are applied in one of two directions: smudging - which is
- * exporting a file from the Git object database to the working directory,
- * and cleaning - which is importing a file from the working directory to
- * the Git object database. These values control which direction of
- * change is being applied.
- */
-typedef enum {
- GIT_FILTER_TO_WORKTREE = 0,
- GIT_FILTER_SMUDGE = GIT_FILTER_TO_WORKTREE,
- GIT_FILTER_TO_ODB = 1,
- GIT_FILTER_CLEAN = GIT_FILTER_TO_ODB,
-} git_filter_mode_t;
-
-/**
- * Filter option flags.
- */
-typedef enum {
- GIT_FILTER_DEFAULT = 0u,
- GIT_FILTER_ALLOW_UNSAFE = (1u << 0),
-} git_filter_flag_t;
-
-/**
- * A filter that can transform file data
- *
- * This represents a filter that can be used to transform or even replace
- * file data. Libgit2 includes one built in filter and it is possible to
- * write your own (see git2/sys/filter.h for information on that).
- *
- * The two builtin filters are:
- *
- * * "crlf" which uses the complex rules with the "text", "eol", and
- * "crlf" file attributes to decide how to convert between LF and CRLF
- * line endings
- * * "ident" which replaces "$Id$" in a blob with "$Id: <blob OID>$" upon
- * checkout and replaced "$Id: <anything>$" with "$Id$" on checkin.
- */
-typedef struct git_filter git_filter;
-
-/**
- * List of filters to be applied
- *
- * This represents a list of filters to be applied to a file / blob. You
- * can build the list with one call, apply it with another, and dispose it
- * with a third. In typical usage, there are not many occasions where a
- * git_filter_list is needed directly since the library will generally
- * handle conversions for you, but it can be convenient to be able to
- * build and apply the list sometimes.
- */
-typedef struct git_filter_list git_filter_list;
-
-/**
- * Load the filter list for a given path.
- *
- * This will return 0 (success) but set the output git_filter_list to NULL
- * if no filters are requested for the given file.
- *
- * @param filters Output newly created git_filter_list (or NULL)
- * @param repo Repository object that contains `path`
- * @param blob The blob to which the filter will be applied (if known)
- * @param path Relative path of the file to be filtered
- * @param mode Filtering direction (WT->ODB or ODB->WT)
- * @param flags Combination of `git_filter_flag_t` flags
- * @return 0 on success (which could still return NULL if no filters are
- * needed for the requested file), <0 on error
- */
-GIT_EXTERN(int) git_filter_list_load(
- git_filter_list **filters,
- git_repository *repo,
- git_blob *blob, /* can be NULL */
- const char *path,
- git_filter_mode_t mode,
- uint32_t flags);
-
-/**
- * Query the filter list to see if a given filter (by name) will run.
- * The built-in filters "crlf" and "ident" can be queried, otherwise this
- * is the name of the filter specified by the filter attribute.
- *
- * This will return 0 if the given filter is not in the list, or 1 if
- * the filter will be applied.
- *
- * @param filters A loaded git_filter_list (or NULL)
- * @param name The name of the filter to query
- * @return 1 if the filter is in the list, 0 otherwise
- */
-GIT_EXTERN(int) git_filter_list_contains(
- git_filter_list *filters,
- const char *name);
-
-/**
- * Apply filter list to a data buffer.
- *
- * See `git2/buffer.h` for background on `git_buf` objects.
- *
- * If the `in` buffer holds data allocated by libgit2 (i.e. `in->asize` is
- * not zero), then it will be overwritten when applying the filters. If
- * not, then it will be left untouched.
- *
- * If there are no filters to apply (or `filters` is NULL), then the `out`
- * buffer will reference the `in` buffer data (with `asize` set to zero)
- * instead of allocating data. This keeps allocations to a minimum, but
- * it means you have to be careful about freeing the `in` data since `out`
- * may be pointing to it!
- *
- * @param out Buffer to store the result of the filtering
- * @param filters A loaded git_filter_list (or NULL)
- * @param in Buffer containing the data to filter
- * @return 0 on success, an error code otherwise
- */
-GIT_EXTERN(int) git_filter_list_apply_to_data(
- git_buf *out,
- git_filter_list *filters,
- git_buf *in);
-
-/**
- * Apply a filter list to the contents of a file on disk
- *
- * @param out buffer into which to store the filtered file
- * @param filters the list of filters to apply
- * @param repo the repository in which to perform the filtering
- * @param path the path of the file to filter, a relative path will be
- * taken as relative to the workdir
- */
-GIT_EXTERN(int) git_filter_list_apply_to_file(
- git_buf *out,
- git_filter_list *filters,
- git_repository *repo,
- const char *path);
-
-/**
- * Apply a filter list to the contents of a blob
- *
- * @param out buffer into which to store the filtered file
- * @param filters the list of filters to apply
- * @param blob the blob to filter
- */
-GIT_EXTERN(int) git_filter_list_apply_to_blob(
- git_buf *out,
- git_filter_list *filters,
- git_blob *blob);
-
-/**
- * Apply a filter list to an arbitrary buffer as a stream
- *
- * @param filters the list of filters to apply
- * @param data the buffer to filter
- * @param target the stream into which the data will be written
- */
-GIT_EXTERN(int) git_filter_list_stream_data(
- git_filter_list *filters,
- git_buf *data,
- git_writestream *target);
-
-/**
- * Apply a filter list to a file as a stream
- *
- * @param filters the list of filters to apply
- * @param repo the repository in which to perform the filtering
- * @param path the path of the file to filter, a relative path will be
- * taken as relative to the workdir
- * @param target the stream into which the data will be written
- */
-GIT_EXTERN(int) git_filter_list_stream_file(
- git_filter_list *filters,
- git_repository *repo,
- const char *path,
- git_writestream *target);
-
-/**
- * Apply a filter list to a blob as a stream
- *
- * @param filters the list of filters to apply
- * @param blob the blob to filter
- * @param target the stream into which the data will be written
- */
-GIT_EXTERN(int) git_filter_list_stream_blob(
- git_filter_list *filters,
- git_blob *blob,
- git_writestream *target);
-
-/**
- * Free a git_filter_list
- *
- * @param filters A git_filter_list created by `git_filter_list_load`
- */
-GIT_EXTERN(void) git_filter_list_free(git_filter_list *filters);
-
-
-GIT_END_DECL
-
-/** @} */
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_global_h__
-#define INCLUDE_git_global_h__
-
-#include "common.h"
-
-GIT_BEGIN_DECL
-
-/**
- * Init the global state
- *
- * This function must the called before any other libgit2 function in
- * order to set up global state and threading.
- *
- * This function may be called multiple times - it will return the number
- * of times the initialization has been called (including this one) that have
- * not subsequently been shutdown.
- *
- * @return the number of initializations of the library, or an error code.
- */
-GIT_EXTERN(int) git_libgit2_init(void);
-
-/**
- * Shutdown the global state
- *
- * Clean up the global state and threading context after calling it as
- * many times as `git_libgit2_init()` was called - it will return the
- * number of remainining initializations that have not been shutdown
- * (after this one).
- *
- * @return the number of remaining initializations of the library, or an
- * error code.
- */
-GIT_EXTERN(int) git_libgit2_shutdown(void);
-
-/** @} */
-GIT_END_DECL
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_graph_h__
-#define INCLUDE_git_graph_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-
-/**
- * @file git2/graph.h
- * @brief Git graph traversal routines
- * @defgroup git_revwalk Git graph traversal routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Count the number of unique commits between two commit objects
- *
- * There is no need for branches containing the commits to have any
- * upstream relationship, but it helps to think of one as a branch and
- * the other as its upstream, the `ahead` and `behind` values will be
- * what git would report for the branches.
- *
- * @param ahead number of unique from commits in `upstream`
- * @param behind number of unique from commits in `local`
- * @param repo the repository where the commits exist
- * @param local the commit for local
- * @param upstream the commit for upstream
- */
-GIT_EXTERN(int) git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream);
-
-
-/**
- * Determine if a commit is the descendant of another commit.
- *
- * @param commit a previously loaded commit.
- * @param ancestor a potential ancestor commit.
- * @return 1 if the given commit is a descendant of the potential ancestor,
- * 0 if not, error code otherwise.
- */
-GIT_EXTERN(int) git_graph_descendant_of(
- git_repository *repo,
- const git_oid *commit,
- const git_oid *ancestor);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_ignore_h__
-#define INCLUDE_git_ignore_h__
-
-#include "common.h"
-#include "types.h"
-
-GIT_BEGIN_DECL
-
-/**
- * Add ignore rules for a repository.
- *
- * Excludesfile rules (i.e. .gitignore rules) are generally read from
- * .gitignore files in the repository tree or from a shared system file
- * only if a "core.excludesfile" config value is set. The library also
- * keeps a set of per-repository internal ignores that can be configured
- * in-memory and will not persist. This function allows you to add to
- * that internal rules list.
- *
- * Example usage:
- *
- * error = git_ignore_add_rule(myrepo, "*.c\ndir/\nFile with space\n");
- *
- * This would add three rules to the ignores.
- *
- * @param repo The repository to add ignore rules to.
- * @param rules Text of rules, a la the contents of a .gitignore file.
- * It is okay to have multiple rules in the text; if so,
- * each rule should be terminated with a newline.
- * @return 0 on success
- */
-GIT_EXTERN(int) git_ignore_add_rule(
- git_repository *repo,
- const char *rules);
-
-/**
- * Clear ignore rules that were explicitly added.
- *
- * Resets to the default internal ignore rules. This will not turn off
- * rules in .gitignore files that actually exist in the filesystem.
- *
- * The default internal ignores ignore ".", ".." and ".git" entries.
- *
- * @param repo The repository to remove ignore rules from.
- * @return 0 on success
- */
-GIT_EXTERN(int) git_ignore_clear_internal_rules(
- git_repository *repo);
-
-/**
- * Test if the ignore rules apply to a given path.
- *
- * This function checks the ignore rules to see if they would apply to the
- * given file. This indicates if the file would be ignored regardless of
- * whether the file is already in the index or committed to the repository.
- *
- * One way to think of this is if you were to do "git add ." on the
- * directory containing the file, would it be added or not?
- *
- * @param ignored boolean returning 0 if the file is not ignored, 1 if it is
- * @param repo a repository object
- * @param path the file to check ignores for, relative to the repo's workdir.
- * @return 0 if ignore rules could be processed for the file (regardless
- * of whether it exists or not), or an error < 0 if they could not.
- */
-GIT_EXTERN(int) git_ignore_path_is_ignored(
- int *ignored,
- git_repository *repo,
- const char *path);
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_index_h__
-#define INCLUDE_git_index_h__
-
-#include "common.h"
-#include "indexer.h"
-#include "types.h"
-#include "oid.h"
-#include "strarray.h"
-
-/**
- * @file git2/index.h
- * @brief Git index parsing and manipulation routines
- * @defgroup git_index Git index parsing and manipulation routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/** Time structure used in a git index entry */
-typedef struct {
- int32_t seconds;
- /* nsec should not be stored as time_t compatible */
- uint32_t nanoseconds;
-} git_index_time;
-
-/**
- * In-memory representation of a file entry in the index.
- *
- * This is a public structure that represents a file entry in the index.
- * The meaning of the fields corresponds to core Git's documentation (in
- * "Documentation/technical/index-format.txt").
- *
- * The `flags` field consists of a number of bit fields which can be
- * accessed via the first set of `GIT_IDXENTRY_...` bitmasks below. These
- * flags are all read from and persisted to disk.
- *
- * The `flags_extended` field also has a number of bit fields which can be
- * accessed via the later `GIT_IDXENTRY_...` bitmasks below. Some of
- * these flags are read from and written to disk, but some are set aside
- * for in-memory only reference.
- *
- * Note that the time and size fields are truncated to 32 bits. This
- * is enough to detect changes, which is enough for the index to
- * function as a cache, but it should not be taken as an authoritative
- * source for that data.
- */
-typedef struct git_index_entry {
- git_index_time ctime;
- git_index_time mtime;
-
- uint32_t dev;
- uint32_t ino;
- uint32_t mode;
- uint32_t uid;
- uint32_t gid;
- uint32_t file_size;
-
- git_oid id;
-
- uint16_t flags;
- uint16_t flags_extended;
-
- const char *path;
-} git_index_entry;
-
-/**
- * Bitmasks for on-disk fields of `git_index_entry`'s `flags`
- *
- * These bitmasks match the four fields in the `git_index_entry` `flags`
- * value both in memory and on disk. You can use them to interpret the
- * data in the `flags`.
- */
-#define GIT_IDXENTRY_NAMEMASK (0x0fff)
-#define GIT_IDXENTRY_STAGEMASK (0x3000)
-#define GIT_IDXENTRY_STAGESHIFT 12
-
-/**
- * Flags for index entries
- */
-typedef enum {
- GIT_IDXENTRY_EXTENDED = (0x4000),
- GIT_IDXENTRY_VALID = (0x8000),
-} git_indxentry_flag_t;
-
-#define GIT_IDXENTRY_STAGE(E) \
- (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT)
-
-#define GIT_IDXENTRY_STAGE_SET(E,S) do { \
- (E)->flags = ((E)->flags & ~GIT_IDXENTRY_STAGEMASK) | \
- (((S) & 0x03) << GIT_IDXENTRY_STAGESHIFT); } while (0)
-
-/**
- * Bitmasks for on-disk fields of `git_index_entry`'s `flags_extended`
- *
- * In memory, the `flags_extended` fields are divided into two parts: the
- * fields that are read from and written to disk, and other fields that
- * in-memory only and used by libgit2. Only the flags in
- * `GIT_IDXENTRY_EXTENDED_FLAGS` will get saved on-disk.
- *
- * Thee first three bitmasks match the three fields in the
- * `git_index_entry` `flags_extended` value that belong on disk. You
- * can use them to interpret the data in the `flags_extended`.
- *
- * The rest of the bitmasks match the other fields in the `git_index_entry`
- * `flags_extended` value that are only used in-memory by libgit2.
- * You can use them to interpret the data in the `flags_extended`.
- *
- */
-typedef enum {
-
- GIT_IDXENTRY_INTENT_TO_ADD = (1 << 13),
- GIT_IDXENTRY_SKIP_WORKTREE = (1 << 14),
- /** Reserved for future extension */
- GIT_IDXENTRY_EXTENDED2 = (1 << 15),
-
- GIT_IDXENTRY_EXTENDED_FLAGS = (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE),
- GIT_IDXENTRY_UPDATE = (1 << 0),
- GIT_IDXENTRY_REMOVE = (1 << 1),
- GIT_IDXENTRY_UPTODATE = (1 << 2),
- GIT_IDXENTRY_ADDED = (1 << 3),
-
- GIT_IDXENTRY_HASHED = (1 << 4),
- GIT_IDXENTRY_UNHASHED = (1 << 5),
- GIT_IDXENTRY_WT_REMOVE = (1 << 6), /**< remove in work directory */
- GIT_IDXENTRY_CONFLICTED = (1 << 7),
-
- GIT_IDXENTRY_UNPACKED = (1 << 8),
- GIT_IDXENTRY_NEW_SKIP_WORKTREE = (1 << 9),
-} git_idxentry_extended_flag_t;
-
-/** Capabilities of system that affect index actions. */
-typedef enum {
- GIT_INDEXCAP_IGNORE_CASE = 1,
- GIT_INDEXCAP_NO_FILEMODE = 2,
- GIT_INDEXCAP_NO_SYMLINKS = 4,
- GIT_INDEXCAP_FROM_OWNER = -1,
-} git_indexcap_t;
-
-/** Callback for APIs that add/remove/update files matching pathspec */
-typedef int (*git_index_matched_path_cb)(
- const char *path, const char *matched_pathspec, void *payload);
-
-/** Flags for APIs that add files matching pathspec */
-typedef enum {
- GIT_INDEX_ADD_DEFAULT = 0,
- GIT_INDEX_ADD_FORCE = (1u << 0),
- GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH = (1u << 1),
- GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2),
-} git_index_add_option_t;
-
-typedef enum {
- /**
- * Match any index stage.
- *
- * Some index APIs take a stage to match; pass this value to match
- * any entry matching the path regardless of stage.
- */
- GIT_INDEX_STAGE_ANY = -1,
-
- /** A normal staged file in the index. */
- GIT_INDEX_STAGE_NORMAL = 0,
-
- /** The ancestor side of a conflict. */
- GIT_INDEX_STAGE_ANCESTOR = 1,
-
- /** The "ours" side of a conflict. */
- GIT_INDEX_STAGE_OURS = 2,
-
- /** The "theirs" side of a conflict. */
- GIT_INDEX_STAGE_THEIRS = 3,
-} git_index_stage_t;
-
-/** @name Index File Functions
- *
- * These functions work on the index file itself.
- */
-/**@{*/
-
-/**
- * Create a new bare Git index object as a memory representation
- * of the Git index file in 'index_path', without a repository
- * to back it.
- *
- * Since there is no ODB or working directory behind this index,
- * any Index methods which rely on these (e.g. index_add_bypath)
- * will fail with the GIT_ERROR error code.
- *
- * If you need to access the index of an actual repository,
- * use the `git_repository_index` wrapper.
- *
- * The index must be freed once it's no longer in use.
- *
- * @param out the pointer for the new index
- * @param index_path the path to the index file in disk
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_open(git_index **out, const char *index_path);
-
-/**
- * Create an in-memory index object.
- *
- * This index object cannot be read/written to the filesystem,
- * but may be used to perform in-memory index operations.
- *
- * The index must be freed once it's no longer in use.
- *
- * @param out the pointer for the new index
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_new(git_index **out);
-
-/**
- * Free an existing index object.
- *
- * @param index an existing index object
- */
-GIT_EXTERN(void) git_index_free(git_index *index);
-
-/**
- * Get the repository this index relates to
- *
- * @param index The index
- * @return A pointer to the repository
- */
-GIT_EXTERN(git_repository *) git_index_owner(const git_index *index);
-
-/**
- * Read index capabilities flags.
- *
- * @param index An existing index object
- * @return A combination of GIT_INDEXCAP values
- */
-GIT_EXTERN(int) git_index_caps(const git_index *index);
-
-/**
- * Set index capabilities flags.
- *
- * If you pass `GIT_INDEXCAP_FROM_OWNER` for the caps, then the
- * capabilities will be read from the config of the owner object,
- * looking at `core.ignorecase`, `core.filemode`, `core.symlinks`.
- *
- * @param index An existing index object
- * @param caps A combination of GIT_INDEXCAP values
- * @return 0 on success, -1 on failure
- */
-GIT_EXTERN(int) git_index_set_caps(git_index *index, int caps);
-
-/**
- * Get index on-disk version.
- *
- * Valid return values are 2, 3, or 4. If 3 is returned, an index
- * with version 2 may be written instead, if the extension data in
- * version 3 is not necessary.
- *
- * @param index An existing index object
- * @return the index version
- */
-GIT_EXTERN(unsigned int) git_index_version(git_index *index);
-
-/**
- * Set index on-disk version.
- *
- * Valid values are 2, 3, or 4. If 2 is given, git_index_write may
- * write an index with version 3 instead, if necessary to accurately
- * represent the index.
- *
- * @param index An existing index object
- * @param version The new version number
- * @return 0 on success, -1 on failure
- */
-GIT_EXTERN(int) git_index_set_version(git_index *index, unsigned int version);
-
-/**
- * Update the contents of an existing index object in memory by reading
- * from the hard disk.
- *
- * If `force` is true, this performs a "hard" read that discards in-memory
- * changes and always reloads the on-disk index data. If there is no
- * on-disk version, the index will be cleared.
- *
- * If `force` is false, this does a "soft" read that reloads the index
- * data from disk only if it has changed since the last time it was
- * loaded. Purely in-memory index data will be untouched. Be aware: if
- * there are changes on disk, unwritten in-memory changes are discarded.
- *
- * @param index an existing index object
- * @param force if true, always reload, vs. only read if file has changed
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_read(git_index *index, int force);
-
-/**
- * Write an existing index object from memory back to disk
- * using an atomic file lock.
- *
- * @param index an existing index object
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_write(git_index *index);
-
-/**
- * Get the full path to the index file on disk.
- *
- * @param index an existing index object
- * @return path to index file or NULL for in-memory index
- */
-GIT_EXTERN(const char *) git_index_path(const git_index *index);
-
-/**
- * Get the checksum of the index
- *
- * This checksum is the SHA-1 hash over the index file (except the
- * last 20 bytes which are the checksum itself). In cases where the
- * index does not exist on-disk, it will be zeroed out.
- *
- * @param index an existing index object
- * @return a pointer to the checksum of the index
- */
-GIT_EXTERN(const git_oid *) git_index_checksum(git_index *index);
-
-/**
- * Read a tree into the index file with stats
- *
- * The current index contents will be replaced by the specified tree.
- *
- * @param index an existing index object
- * @param tree tree to read
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_read_tree(git_index *index, const git_tree *tree);
-
-/**
- * Write the index as a tree
- *
- * This method will scan the index and write a representation
- * of its current state back to disk; it recursively creates
- * tree objects for each of the subtrees stored in the index,
- * but only returns the OID of the root tree. This is the OID
- * that can be used e.g. to create a commit.
- *
- * The index instance cannot be bare, and needs to be associated
- * to an existing repository.
- *
- * The index must not contain any file in conflict.
- *
- * @param out Pointer where to store the OID of the written tree
- * @param index Index to write
- * @return 0 on success, GIT_EUNMERGED when the index is not clean
- * or an error code
- */
-GIT_EXTERN(int) git_index_write_tree(git_oid *out, git_index *index);
-
-/**
- * Write the index as a tree to the given repository
- *
- * This method will do the same as `git_index_write_tree`, but
- * letting the user choose the repository where the tree will
- * be written.
- *
- * The index must not contain any file in conflict.
- *
- * @param out Pointer where to store OID of the the written tree
- * @param index Index to write
- * @param repo Repository where to write the tree
- * @return 0 on success, GIT_EUNMERGED when the index is not clean
- * or an error code
- */
-GIT_EXTERN(int) git_index_write_tree_to(git_oid *out, git_index *index, git_repository *repo);
-
-/**@}*/
-
-/** @name Raw Index Entry Functions
- *
- * These functions work on index entries, and allow for raw manipulation
- * of the entries.
- */
-/**@{*/
-
-/* Index entry manipulation */
-
-/**
- * Get the count of entries currently in the index
- *
- * @param index an existing index object
- * @return integer of count of current entries
- */
-GIT_EXTERN(size_t) git_index_entrycount(const git_index *index);
-
-/**
- * Clear the contents (all the entries) of an index object.
- *
- * This clears the index object in memory; changes must be explicitly
- * written to disk for them to take effect persistently.
- *
- * @param index an existing index object
- * @return 0 on success, error code < 0 on failure
- */
-GIT_EXTERN(int) git_index_clear(git_index *index);
-
-/**
- * Get a pointer to one of the entries in the index
- *
- * The entry is not modifiable and should not be freed. Because the
- * `git_index_entry` struct is a publicly defined struct, you should
- * be able to make your own permanent copy of the data if necessary.
- *
- * @param index an existing index object
- * @param n the position of the entry
- * @return a pointer to the entry; NULL if out of bounds
- */
-GIT_EXTERN(const git_index_entry *) git_index_get_byindex(
- git_index *index, size_t n);
-
-/**
- * Get a pointer to one of the entries in the index
- *
- * The entry is not modifiable and should not be freed. Because the
- * `git_index_entry` struct is a publicly defined struct, you should
- * be able to make your own permanent copy of the data if necessary.
- *
- * @param index an existing index object
- * @param path path to search
- * @param stage stage to search
- * @return a pointer to the entry; NULL if it was not found
- */
-GIT_EXTERN(const git_index_entry *) git_index_get_bypath(
- git_index *index, const char *path, int stage);
-
-/**
- * Remove an entry from the index
- *
- * @param index an existing index object
- * @param path path to search
- * @param stage stage to search
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_remove(git_index *index, const char *path, int stage);
-
-/**
- * Remove all entries from the index under a given directory
- *
- * @param index an existing index object
- * @param dir container directory path
- * @param stage stage to search
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_remove_directory(
- git_index *index, const char *dir, int stage);
-
-/**
- * Add or update an index entry from an in-memory struct
- *
- * If a previous index entry exists that has the same path and stage
- * as the given 'source_entry', it will be replaced. Otherwise, the
- * 'source_entry' will be added.
- *
- * A full copy (including the 'path' string) of the given
- * 'source_entry' will be inserted on the index.
- *
- * @param index an existing index object
- * @param source_entry new entry object
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_entry);
-
-/**
- * Return the stage number from a git index entry
- *
- * This entry is calculated from the entry's flag attribute like this:
- *
- * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
- *
- * @param entry The entry
- * @return the stage number
- */
-GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
-
-/**
- * Return whether the given index entry is a conflict (has a high stage
- * entry). This is simply shorthand for `git_index_entry_stage > 0`.
- *
- * @param entry The entry
- * @return 1 if the entry is a conflict entry, 0 otherwise
- */
-GIT_EXTERN(int) git_index_entry_is_conflict(const git_index_entry *entry);
-
-/**@}*/
-
-/** @name Workdir Index Entry Functions
- *
- * These functions work on index entries specifically in the working
- * directory (ie, stage 0).
- */
-/**@{*/
-
-/**
- * Add or update an index entry from a file on disk
- *
- * The file `path` must be relative to the repository's
- * working folder and must be readable.
- *
- * This method will fail in bare index instances.
- *
- * This forces the file to be added to the index, not looking
- * at gitignore rules. Those rules can be evaluated through
- * the git_status APIs (in status.h) before calling this.
- *
- * If this file currently is the result of a merge conflict, this
- * file will no longer be marked as conflicting. The data about
- * the conflict will be moved to the "resolve undo" (REUC) section.
- *
- * @param index an existing index object
- * @param path filename to add
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_add_bypath(git_index *index, const char *path);
-
-/**
- * Add or update an index entry from a buffer in memory
- *
- * This method will create a blob in the repository that owns the
- * index and then add the index entry to the index. The `path` of the
- * entry represents the position of the blob relative to the
- * repository's root folder.
- *
- * If a previous index entry exists that has the same path as the
- * given 'entry', it will be replaced. Otherwise, the 'entry' will be
- * added. The `id` and the `file_size` of the 'entry' are updated with the
- * real value of the blob.
- *
- * This forces the file to be added to the index, not looking
- * at gitignore rules. Those rules can be evaluated through
- * the git_status APIs (in status.h) before calling this.
- *
- * If this file currently is the result of a merge conflict, this
- * file will no longer be marked as conflicting. The data about
- * the conflict will be moved to the "resolve undo" (REUC) section.
- *
- * @param index an existing index object
- * @param entry filename to add
- * @param buffer data to be written into the blob
- * @param len length of the data
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_add_frombuffer(
- git_index *index,
- const git_index_entry *entry,
- const void *buffer, size_t len);
-
-/**
- * Remove an index entry corresponding to a file on disk
- *
- * The file `path` must be relative to the repository's
- * working folder. It may exist.
- *
- * If this file currently is the result of a merge conflict, this
- * file will no longer be marked as conflicting. The data about
- * the conflict will be moved to the "resolve undo" (REUC) section.
- *
- * @param index an existing index object
- * @param path filename to remove
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path);
-
-/**
- * Add or update index entries matching files in the working directory.
- *
- * This method will fail in bare index instances.
- *
- * The `pathspec` is a list of file names or shell glob patterns that will
- * matched against files in the repository's working directory. Each file
- * that matches will be added to the index (either updating an existing
- * entry or adding a new entry). You can disable glob expansion and force
- * exact matching with the `GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH` flag.
- *
- * Files that are ignored will be skipped (unlike `git_index_add_bypath`).
- * If a file is already tracked in the index, then it *will* be updated
- * even if it is ignored. Pass the `GIT_INDEX_ADD_FORCE` flag to
- * skip the checking of ignore rules.
- *
- * To emulate `git add -A` and generate an error if the pathspec contains
- * the exact path of an ignored file (when not using FORCE), add the
- * `GIT_INDEX_ADD_CHECK_PATHSPEC` flag. This checks that each entry
- * in the `pathspec` that is an exact match to a filename on disk is
- * either not ignored or already in the index. If this check fails, the
- * function will return GIT_EINVALIDSPEC.
- *
- * To emulate `git add -A` with the "dry-run" option, just use a callback
- * function that always returns a positive value. See below for details.
- *
- * If any files are currently the result of a merge conflict, those files
- * will no longer be marked as conflicting. The data about the conflicts
- * will be moved to the "resolve undo" (REUC) section.
- *
- * If you provide a callback function, it will be invoked on each matching
- * item in the working directory immediately *before* it is added to /
- * updated in the index. Returning zero will add the item to the index,
- * greater than zero will skip the item, and less than zero will abort the
- * scan and return that value to the caller.
- *
- * @param index an existing index object
- * @param pathspec array of path patterns
- * @param flags combination of git_index_add_option_t flags
- * @param callback notification callback for each added/updated path (also
- * gets index of matching pathspec entry); can be NULL;
- * return 0 to add, >0 to skip, <0 to abort scan.
- * @param payload payload passed through to callback function
- * @return 0 on success, negative callback return value, or error code
- */
-GIT_EXTERN(int) git_index_add_all(
- git_index *index,
- const git_strarray *pathspec,
- unsigned int flags,
- git_index_matched_path_cb callback,
- void *payload);
-
-/**
- * Remove all matching index entries.
- *
- * If you provide a callback function, it will be invoked on each matching
- * item in the index immediately *before* it is removed. Return 0 to
- * remove the item, > 0 to skip the item, and < 0 to abort the scan.
- *
- * @param index An existing index object
- * @param pathspec array of path patterns
- * @param callback notification callback for each removed path (also
- * gets index of matching pathspec entry); can be NULL;
- * return 0 to add, >0 to skip, <0 to abort scan.
- * @param payload payload passed through to callback function
- * @return 0 on success, negative callback return value, or error code
- */
-GIT_EXTERN(int) git_index_remove_all(
- git_index *index,
- const git_strarray *pathspec,
- git_index_matched_path_cb callback,
- void *payload);
-
-/**
- * Update all index entries to match the working directory
- *
- * This method will fail in bare index instances.
- *
- * This scans the existing index entries and synchronizes them with the
- * working directory, deleting them if the corresponding working directory
- * file no longer exists otherwise updating the information (including
- * adding the latest version of file to the ODB if needed).
- *
- * If you provide a callback function, it will be invoked on each matching
- * item in the index immediately *before* it is updated (either refreshed
- * or removed depending on working directory state). Return 0 to proceed
- * with updating the item, > 0 to skip the item, and < 0 to abort the scan.
- *
- * @param index An existing index object
- * @param pathspec array of path patterns
- * @param callback notification callback for each updated path (also
- * gets index of matching pathspec entry); can be NULL;
- * return 0 to add, >0 to skip, <0 to abort scan.
- * @param payload payload passed through to callback function
- * @return 0 on success, negative callback return value, or error code
- */
-GIT_EXTERN(int) git_index_update_all(
- git_index *index,
- const git_strarray *pathspec,
- git_index_matched_path_cb callback,
- void *payload);
-
-/**
- * Find the first position of any entries which point to given
- * path in the Git index.
- *
- * @param at_pos the address to which the position of the index entry is written (optional)
- * @param index an existing index object
- * @param path path to search
- * @return a zero-based position in the index if found; GIT_ENOTFOUND otherwise
- */
-GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path);
-
-/**
- * Find the first position of any entries matching a prefix. To find the first position
- * of a path inside a given folder, suffix the prefix with a '/'.
- *
- * @param at_pos the address to which the position of the index entry is written (optional)
- * @param index an existing index object
- * @param prefix the prefix to search for
- * @return 0 with valid value in at_pos; an error code otherwise
- */
-GIT_EXTERN(int) git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix);
-
-/**@}*/
-
-/** @name Conflict Index Entry Functions
- *
- * These functions work on conflict index entries specifically (ie, stages 1-3)
- */
-/**@{*/
-
-/**
- * Add or update index entries to represent a conflict. Any staged
- * entries that exist at the given paths will be removed.
- *
- * The entries are the entries from the tree included in the merge. Any
- * entry may be null to indicate that that file was not present in the
- * trees during the merge. For example, ancestor_entry may be NULL to
- * indicate that a file was added in both branches and must be resolved.
- *
- * @param index an existing index object
- * @param ancestor_entry the entry data for the ancestor of the conflict
- * @param our_entry the entry data for our side of the merge conflict
- * @param their_entry the entry data for their side of the merge conflict
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_conflict_add(
- git_index *index,
- const git_index_entry *ancestor_entry,
- const git_index_entry *our_entry,
- const git_index_entry *their_entry);
-
-/**
- * Get the index entries that represent a conflict of a single file.
- *
- * The entries are not modifiable and should not be freed. Because the
- * `git_index_entry` struct is a publicly defined struct, you should
- * be able to make your own permanent copy of the data if necessary.
- *
- * @param ancestor_out Pointer to store the ancestor entry
- * @param our_out Pointer to store the our entry
- * @param their_out Pointer to store the their entry
- * @param index an existing index object
- * @param path path to search
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_conflict_get(
- const git_index_entry **ancestor_out,
- const git_index_entry **our_out,
- const git_index_entry **their_out,
- git_index *index,
- const char *path);
-
-/**
- * Removes the index entries that represent a conflict of a single file.
- *
- * @param index an existing index object
- * @param path path to remove conflicts for
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_conflict_remove(git_index *index, const char *path);
-
-/**
- * Remove all conflicts in the index (entries with a stage greater than 0).
- *
- * @param index an existing index object
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_conflict_cleanup(git_index *index);
-
-/**
- * Determine if the index contains entries representing file conflicts.
- *
- * @return 1 if at least one conflict is found, 0 otherwise.
- */
-GIT_EXTERN(int) git_index_has_conflicts(const git_index *index);
-
-/**
- * Create an iterator for the conflicts in the index.
- *
- * The index must not be modified while iterating; the results are undefined.
- *
- * @param iterator_out The newly created conflict iterator
- * @param index The index to scan
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_conflict_iterator_new(
- git_index_conflict_iterator **iterator_out,
- git_index *index);
-
-/**
- * Returns the current conflict (ancestor, ours and theirs entry) and
- * advance the iterator internally to the next value.
- *
- * @param ancestor_out Pointer to store the ancestor side of the conflict
- * @param our_out Pointer to store our side of the conflict
- * @param their_out Pointer to store their side of the conflict
- * @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code
- * (negative value)
- */
-GIT_EXTERN(int) git_index_conflict_next(
- const git_index_entry **ancestor_out,
- const git_index_entry **our_out,
- const git_index_entry **their_out,
- git_index_conflict_iterator *iterator);
-
-/**
- * Frees a `git_index_conflict_iterator`.
- *
- * @param iterator pointer to the iterator
- */
-GIT_EXTERN(void) git_index_conflict_iterator_free(
- git_index_conflict_iterator *iterator);
-
-/**@}*/
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef _INCLUDE_git_indexer_h__
-#define _INCLUDE_git_indexer_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-
-GIT_BEGIN_DECL
-
-typedef struct git_indexer git_indexer;
-
-/**
- * Create a new indexer instance
- *
- * @param out where to store the indexer instance
- * @param path to the directory where the packfile should be stored
- * @param mode permissions to use creating packfile or 0 for defaults
- * @param odb object database from which to read base objects when
- * fixing thin packs. Pass NULL if no thin pack is expected (an error
- * will be returned if there are bases missing)
- * @param progress_cb function to call with progress information
- * @param progress_cb_payload payload for the progress callback
- */
-GIT_EXTERN(int) git_indexer_new(
- git_indexer **out,
- const char *path,
- unsigned int mode,
- git_odb *odb,
- git_transfer_progress_cb progress_cb,
- void *progress_cb_payload);
-
-/**
- * Add data to the indexer
- *
- * @param idx the indexer
- * @param data the data to add
- * @param size the size of the data in bytes
- * @param stats stat storage
- */
-GIT_EXTERN(int) git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats);
-
-/**
- * Finalize the pack and index
- *
- * Resolve any pending deltas and write out the index file
- *
- * @param idx the indexer
- */
-GIT_EXTERN(int) git_indexer_commit(git_indexer *idx, git_transfer_progress *stats);
-
-/**
- * Get the packfile's hash
- *
- * A packfile's name is derived from the sorted hashing of all object
- * names. This is only correct after the index has been finalized.
- *
- * @param idx the indexer instance
- */
-GIT_EXTERN(const git_oid *) git_indexer_hash(const git_indexer *idx);
-
-/**
- * Free the indexer and its resources
- *
- * @param idx the indexer to free
- */
-GIT_EXTERN(void) git_indexer_free(git_indexer *idx);
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-// ISO C9x compliant inttypes.h for Microsoft Visual Studio
-// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
-//
-// Copyright (c) 2006 Alexander Chemeris
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// 3. The name of the author may be used to endorse or promote products
-// derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef _MSC_VER // [
-#error "Use this header only with Microsoft Visual C++ compilers!"
-#endif // _MSC_VER ]
-
-#ifndef _MSC_INTTYPES_H_ // [
-#define _MSC_INTTYPES_H_
-
-#if _MSC_VER > 1000
-#pragma once
-#endif
-
-#if _MSC_VER >= 1600
-#include <stdint.h>
-#else
-#include "stdint.h"
-#endif
-
-// 7.8 Format conversion of integer types
-
-typedef struct {
- intmax_t quot;
- intmax_t rem;
-} imaxdiv_t;
-
-// 7.8.1 Macros for format specifiers
-
-#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
-
-// The fprintf macros for signed integers are:
-#define PRId8 "d"
-#define PRIi8 "i"
-#define PRIdLEAST8 "d"
-#define PRIiLEAST8 "i"
-#define PRIdFAST8 "d"
-#define PRIiFAST8 "i"
-
-#define PRId16 "hd"
-#define PRIi16 "hi"
-#define PRIdLEAST16 "hd"
-#define PRIiLEAST16 "hi"
-#define PRIdFAST16 "hd"
-#define PRIiFAST16 "hi"
-
-#define PRId32 "I32d"
-#define PRIi32 "I32i"
-#define PRIdLEAST32 "I32d"
-#define PRIiLEAST32 "I32i"
-#define PRIdFAST32 "I32d"
-#define PRIiFAST32 "I32i"
-
-#define PRId64 "I64d"
-#define PRIi64 "I64i"
-#define PRIdLEAST64 "I64d"
-#define PRIiLEAST64 "I64i"
-#define PRIdFAST64 "I64d"
-#define PRIiFAST64 "I64i"
-
-#define PRIdMAX "I64d"
-#define PRIiMAX "I64i"
-
-#define PRIdPTR "Id"
-#define PRIiPTR "Ii"
-
-// The fprintf macros for unsigned integers are:
-#define PRIo8 "o"
-#define PRIu8 "u"
-#define PRIx8 "x"
-#define PRIX8 "X"
-#define PRIoLEAST8 "o"
-#define PRIuLEAST8 "u"
-#define PRIxLEAST8 "x"
-#define PRIXLEAST8 "X"
-#define PRIoFAST8 "o"
-#define PRIuFAST8 "u"
-#define PRIxFAST8 "x"
-#define PRIXFAST8 "X"
-
-#define PRIo16 "ho"
-#define PRIu16 "hu"
-#define PRIx16 "hx"
-#define PRIX16 "hX"
-#define PRIoLEAST16 "ho"
-#define PRIuLEAST16 "hu"
-#define PRIxLEAST16 "hx"
-#define PRIXLEAST16 "hX"
-#define PRIoFAST16 "ho"
-#define PRIuFAST16 "hu"
-#define PRIxFAST16 "hx"
-#define PRIXFAST16 "hX"
-
-#define PRIo32 "I32o"
-#define PRIu32 "I32u"
-#define PRIx32 "I32x"
-#define PRIX32 "I32X"
-#define PRIoLEAST32 "I32o"
-#define PRIuLEAST32 "I32u"
-#define PRIxLEAST32 "I32x"
-#define PRIXLEAST32 "I32X"
-#define PRIoFAST32 "I32o"
-#define PRIuFAST32 "I32u"
-#define PRIxFAST32 "I32x"
-#define PRIXFAST32 "I32X"
-
-#define PRIo64 "I64o"
-#define PRIu64 "I64u"
-#define PRIx64 "I64x"
-#define PRIX64 "I64X"
-#define PRIoLEAST64 "I64o"
-#define PRIuLEAST64 "I64u"
-#define PRIxLEAST64 "I64x"
-#define PRIXLEAST64 "I64X"
-#define PRIoFAST64 "I64o"
-#define PRIuFAST64 "I64u"
-#define PRIxFAST64 "I64x"
-#define PRIXFAST64 "I64X"
-
-#define PRIoMAX "I64o"
-#define PRIuMAX "I64u"
-#define PRIxMAX "I64x"
-#define PRIXMAX "I64X"
-
-#define PRIoPTR "Io"
-#define PRIuPTR "Iu"
-#define PRIxPTR "Ix"
-#define PRIXPTR "IX"
-
-// The fscanf macros for signed integers are:
-#define SCNd8 "d"
-#define SCNi8 "i"
-#define SCNdLEAST8 "d"
-#define SCNiLEAST8 "i"
-#define SCNdFAST8 "d"
-#define SCNiFAST8 "i"
-
-#define SCNd16 "hd"
-#define SCNi16 "hi"
-#define SCNdLEAST16 "hd"
-#define SCNiLEAST16 "hi"
-#define SCNdFAST16 "hd"
-#define SCNiFAST16 "hi"
-
-#define SCNd32 "ld"
-#define SCNi32 "li"
-#define SCNdLEAST32 "ld"
-#define SCNiLEAST32 "li"
-#define SCNdFAST32 "ld"
-#define SCNiFAST32 "li"
-
-#define SCNd64 "I64d"
-#define SCNi64 "I64i"
-#define SCNdLEAST64 "I64d"
-#define SCNiLEAST64 "I64i"
-#define SCNdFAST64 "I64d"
-#define SCNiFAST64 "I64i"
-
-#define SCNdMAX "I64d"
-#define SCNiMAX "I64i"
-
-#ifdef _WIN64 // [
-# define SCNdPTR "I64d"
-# define SCNiPTR "I64i"
-#else // _WIN64 ][
-# define SCNdPTR "ld"
-# define SCNiPTR "li"
-#endif // _WIN64 ]
-
-// The fscanf macros for unsigned integers are:
-#define SCNo8 "o"
-#define SCNu8 "u"
-#define SCNx8 "x"
-#define SCNX8 "X"
-#define SCNoLEAST8 "o"
-#define SCNuLEAST8 "u"
-#define SCNxLEAST8 "x"
-#define SCNXLEAST8 "X"
-#define SCNoFAST8 "o"
-#define SCNuFAST8 "u"
-#define SCNxFAST8 "x"
-#define SCNXFAST8 "X"
-
-#define SCNo16 "ho"
-#define SCNu16 "hu"
-#define SCNx16 "hx"
-#define SCNX16 "hX"
-#define SCNoLEAST16 "ho"
-#define SCNuLEAST16 "hu"
-#define SCNxLEAST16 "hx"
-#define SCNXLEAST16 "hX"
-#define SCNoFAST16 "ho"
-#define SCNuFAST16 "hu"
-#define SCNxFAST16 "hx"
-#define SCNXFAST16 "hX"
-
-#define SCNo32 "lo"
-#define SCNu32 "lu"
-#define SCNx32 "lx"
-#define SCNX32 "lX"
-#define SCNoLEAST32 "lo"
-#define SCNuLEAST32 "lu"
-#define SCNxLEAST32 "lx"
-#define SCNXLEAST32 "lX"
-#define SCNoFAST32 "lo"
-#define SCNuFAST32 "lu"
-#define SCNxFAST32 "lx"
-#define SCNXFAST32 "lX"
-
-#define SCNo64 "I64o"
-#define SCNu64 "I64u"
-#define SCNx64 "I64x"
-#define SCNX64 "I64X"
-#define SCNoLEAST64 "I64o"
-#define SCNuLEAST64 "I64u"
-#define SCNxLEAST64 "I64x"
-#define SCNXLEAST64 "I64X"
-#define SCNoFAST64 "I64o"
-#define SCNuFAST64 "I64u"
-#define SCNxFAST64 "I64x"
-#define SCNXFAST64 "I64X"
-
-#define SCNoMAX "I64o"
-#define SCNuMAX "I64u"
-#define SCNxMAX "I64x"
-#define SCNXMAX "I64X"
-
-#ifdef _WIN64 // [
-# define SCNoPTR "I64o"
-# define SCNuPTR "I64u"
-# define SCNxPTR "I64x"
-# define SCNXPTR "I64X"
-#else // _WIN64 ][
-# define SCNoPTR "lo"
-# define SCNuPTR "lu"
-# define SCNxPTR "lx"
-# define SCNXPTR "lX"
-#endif // _WIN64 ]
-
-#endif // __STDC_FORMAT_MACROS ]
-
-// 7.8.2 Functions for greatest-width integer types
-
-// 7.8.2.1 The imaxabs function
-#define imaxabs _abs64
-
-// 7.8.2.2 The imaxdiv function
-
-// This is modified version of div() function from Microsoft's div.c found
-// in %MSVC.NET%\crt\src\div.c
-#ifdef STATIC_IMAXDIV // [
-static
-#else // STATIC_IMAXDIV ][
-_inline
-#endif // STATIC_IMAXDIV ]
-imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
-{
- imaxdiv_t result;
-
- result.quot = numer / denom;
- result.rem = numer % denom;
-
- if (numer < 0 && result.rem > 0) {
- // did division wrong; must fix up
- ++result.quot;
- result.rem -= denom;
- }
-
- return result;
-}
-
-// 7.8.2.3 The strtoimax and strtoumax functions
-#define strtoimax _strtoi64
-#define strtoumax _strtoui64
-
-// 7.8.2.4 The wcstoimax and wcstoumax functions
-#define wcstoimax _wcstoi64
-#define wcstoumax _wcstoui64
-
-
-#endif // _MSC_INTTYPES_H_ ]
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_merge_h__
-#define INCLUDE_git_merge_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "oidarray.h"
-#include "checkout.h"
-#include "index.h"
-#include "annotated_commit.h"
-
-/**
- * @file git2/merge.h
- * @brief Git merge routines
- * @defgroup git_merge Git merge routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * The file inputs to `git_merge_file`. Callers should populate the
- * `git_merge_file_input` structure with descriptions of the files in
- * each side of the conflict for use in producing the merge file.
- */
-typedef struct {
- unsigned int version;
-
- /** Pointer to the contents of the file. */
- const char *ptr;
-
- /** Size of the contents pointed to in `ptr`. */
- size_t size;
-
- /** File name of the conflicted file, or `NULL` to not merge the path. */
- const char *path;
-
- /** File mode of the conflicted file, or `0` to not merge the mode. */
- unsigned int mode;
-} git_merge_file_input;
-
-#define GIT_MERGE_FILE_INPUT_VERSION 1
-#define GIT_MERGE_FILE_INPUT_INIT {GIT_MERGE_FILE_INPUT_VERSION}
-
-/**
- * Initializes a `git_merge_file_input` with default values. Equivalent to
- * creating an instance with GIT_MERGE_FILE_INPUT_INIT.
- *
- * @param opts the `git_merge_file_input` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_MERGE_FILE_INPUT_VERSION` here.
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_merge_file_init_input(
- git_merge_file_input *opts,
- unsigned int version);
-
-/**
- * Flags for `git_merge` options. A combination of these flags can be
- * passed in via the `flags` value in the `git_merge_options`.
- */
-typedef enum {
- /**
- * Detect renames that occur between the common ancestor and the "ours"
- * side or the common ancestor and the "theirs" side. This will enable
- * the ability to merge between a modified and renamed file.
- */
- GIT_MERGE_FIND_RENAMES = (1 << 0),
-
- /**
- * If a conflict occurs, exit immediately instead of attempting to
- * continue resolving conflicts. The merge operation will fail with
- * GIT_EMERGECONFLICT and no index will be returned.
- */
- GIT_MERGE_FAIL_ON_CONFLICT = (1 << 1),
-
- /**
- * Do not write the REUC extension on the generated index
- */
- GIT_MERGE_SKIP_REUC = (1 << 2),
-
- /**
- * If the commits being merged have multiple merge bases, do not build
- * a recursive merge base (by merging the multiple merge bases),
- * instead simply use the first base. This flag provides a similar
- * merge base to `git-merge-resolve`.
- */
- GIT_MERGE_NO_RECURSIVE = (1 << 3),
-} git_merge_flag_t;
-
-/**
- * Merge file favor options for `git_merge_options` instruct the file-level
- * merging functionality how to deal with conflicting regions of the files.
- */
-typedef enum {
- /**
- * When a region of a file is changed in both branches, a conflict
- * will be recorded in the index so that `git_checkout` can produce
- * a merge file with conflict markers in the working directory.
- * This is the default.
- */
- GIT_MERGE_FILE_FAVOR_NORMAL = 0,
-
- /**
- * When a region of a file is changed in both branches, the file
- * created in the index will contain the "ours" side of any conflicting
- * region. The index will not record a conflict.
- */
- GIT_MERGE_FILE_FAVOR_OURS = 1,
-
- /**
- * When a region of a file is changed in both branches, the file
- * created in the index will contain the "theirs" side of any conflicting
- * region. The index will not record a conflict.
- */
- GIT_MERGE_FILE_FAVOR_THEIRS = 2,
-
- /**
- * When a region of a file is changed in both branches, the file
- * created in the index will contain each unique line from each side,
- * which has the result of combining both files. The index will not
- * record a conflict.
- */
- GIT_MERGE_FILE_FAVOR_UNION = 3,
-} git_merge_file_favor_t;
-
-/**
- * File merging flags
- */
-typedef enum {
- /** Defaults */
- GIT_MERGE_FILE_DEFAULT = 0,
-
- /** Create standard conflicted merge files */
- GIT_MERGE_FILE_STYLE_MERGE = (1 << 0),
-
- /** Create diff3-style files */
- GIT_MERGE_FILE_STYLE_DIFF3 = (1 << 1),
-
- /** Condense non-alphanumeric regions for simplified diff file */
- GIT_MERGE_FILE_SIMPLIFY_ALNUM = (1 << 2),
-
- /** Ignore all whitespace */
- GIT_MERGE_FILE_IGNORE_WHITESPACE = (1 << 3),
-
- /** Ignore changes in amount of whitespace */
- GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE = (1 << 4),
-
- /** Ignore whitespace at end of line */
- GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL = (1 << 5),
-
- /** Use the "patience diff" algorithm */
- GIT_MERGE_FILE_DIFF_PATIENCE = (1 << 6),
-
- /** Take extra time to find minimal diff */
- GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7),
-} git_merge_file_flag_t;
-
-/**
- * Options for merging a file
- */
-typedef struct {
- unsigned int version;
-
- /**
- * Label for the ancestor file side of the conflict which will be prepended
- * to labels in diff3-format merge files.
- */
- const char *ancestor_label;
-
- /**
- * Label for our file side of the conflict which will be prepended
- * to labels in merge files.
- */
- const char *our_label;
-
- /**
- * Label for their file side of the conflict which will be prepended
- * to labels in merge files.
- */
- const char *their_label;
-
- /** The file to favor in region conflicts. */
- git_merge_file_favor_t favor;
-
- /** see `git_merge_file_flag_t` above */
- git_merge_file_flag_t flags;
-} git_merge_file_options;
-
-#define GIT_MERGE_FILE_OPTIONS_VERSION 1
-#define GIT_MERGE_FILE_OPTIONS_INIT {GIT_MERGE_FILE_OPTIONS_VERSION}
-
-/**
- * Initializes a `git_merge_file_options` with default values. Equivalent to
- * creating an instance with GIT_MERGE_FILE_OPTIONS_INIT.
- *
- * @param opts the `git_merge_file_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_MERGE_FILE_OPTIONS_VERSION` here.
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_merge_file_init_options(
- git_merge_file_options *opts,
- unsigned int version);
-
-/**
- * Information about file-level merging
- */
-typedef struct {
- /**
- * True if the output was automerged, false if the output contains
- * conflict markers.
- */
- unsigned int automergeable;
-
- /**
- * The path that the resultant merge file should use, or NULL if a
- * filename conflict would occur.
- */
- const char *path;
-
- /** The mode that the resultant merge file should use. */
- unsigned int mode;
-
- /** The contents of the merge. */
- const char *ptr;
-
- /** The length of the merge contents. */
- size_t len;
-} git_merge_file_result;
-
-/**
- * Merging options
- */
-typedef struct {
- unsigned int version;
-
- /** See `git_merge_flag_t` above */
- git_merge_flag_t flags;
-
- /**
- * Similarity to consider a file renamed (default 50). If
- * `GIT_MERGE_FIND_RENAMES` is enabled, added files will be compared
- * with deleted files to determine their similarity. Files that are
- * more similar than the rename threshold (percentage-wise) will be
- * treated as a rename.
- */
- unsigned int rename_threshold;
-
- /**
- * Maximum similarity sources to examine for renames (default 200).
- * If the number of rename candidates (add / delete pairs) is greater
- * than this value, inexact rename detection is aborted.
- *
- * This setting overrides the `merge.renameLimit` configuration value.
- */
- unsigned int target_limit;
-
- /** Pluggable similarity metric; pass NULL to use internal metric */
- git_diff_similarity_metric *metric;
-
- /**
- * Maximum number of times to merge common ancestors to build a
- * virtual merge base when faced with criss-cross merges. When this
- * limit is reached, the next ancestor will simply be used instead of
- * attempting to merge it. The default is unlimited.
- */
- unsigned int recursion_limit;
-
- /**
- * Default merge driver to be used when both sides of a merge have
- * changed. The default is the `text` driver.
- */
- const char *default_driver;
-
- /**
- * Flags for handling conflicting content, to be used with the standard
- * (`text`) merge driver.
- */
- git_merge_file_favor_t file_favor;
-
- /** see `git_merge_file_flag_t` above */
- git_merge_file_flag_t file_flags;
-} git_merge_options;
-
-#define GIT_MERGE_OPTIONS_VERSION 1
-#define GIT_MERGE_OPTIONS_INIT {GIT_MERGE_OPTIONS_VERSION}
-
-/**
- * Initializes a `git_merge_options` with default values. Equivalent to
- * creating an instance with GIT_MERGE_OPTIONS_INIT.
- *
- * @param opts the `git_merge_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_MERGE_OPTIONS_VERSION` here.
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_merge_init_options(
- git_merge_options *opts,
- unsigned int version);
-
-/**
- * The results of `git_merge_analysis` indicate the merge opportunities.
- */
-typedef enum {
- /** No merge is possible. (Unused.) */
- GIT_MERGE_ANALYSIS_NONE = 0,
-
- /**
- * A "normal" merge; both HEAD and the given merge input have diverged
- * from their common ancestor. The divergent commits must be merged.
- */
- GIT_MERGE_ANALYSIS_NORMAL = (1 << 0),
-
- /**
- * All given merge inputs are reachable from HEAD, meaning the
- * repository is up-to-date and no merge needs to be performed.
- */
- GIT_MERGE_ANALYSIS_UP_TO_DATE = (1 << 1),
-
- /**
- * The given merge input is a fast-forward from HEAD and no merge
- * needs to be performed. Instead, the client can check out the
- * given merge input.
- */
- GIT_MERGE_ANALYSIS_FASTFORWARD = (1 << 2),
-
- /**
- * The HEAD of the current repository is "unborn" and does not point to
- * a valid commit. No merge can be performed, but the caller may wish
- * to simply set HEAD to the target commit(s).
- */
- GIT_MERGE_ANALYSIS_UNBORN = (1 << 3),
-} git_merge_analysis_t;
-
-/**
- * The user's stated preference for merges.
- */
-typedef enum {
- /**
- * No configuration was found that suggests a preferred behavior for
- * merge.
- */
- GIT_MERGE_PREFERENCE_NONE = 0,
-
- /**
- * There is a `merge.ff=false` configuration setting, suggesting that
- * the user does not want to allow a fast-forward merge.
- */
- GIT_MERGE_PREFERENCE_NO_FASTFORWARD = (1 << 0),
-
- /**
- * There is a `merge.ff=only` configuration setting, suggesting that
- * the user only wants fast-forward merges.
- */
- GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY = (1 << 1),
-} git_merge_preference_t;
-
-/**
- * Analyzes the given branch(es) and determines the opportunities for
- * merging them into the HEAD of the repository.
- *
- * @param analysis_out analysis enumeration that the result is written into
- * @param repo the repository to merge
- * @param their_heads the heads to merge into
- * @param their_heads_len the number of heads to merge
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_merge_analysis(
- git_merge_analysis_t *analysis_out,
- git_merge_preference_t *preference_out,
- git_repository *repo,
- const git_annotated_commit **their_heads,
- size_t their_heads_len);
-
-/**
- * Find a merge base between two commits
- *
- * @param out the OID of a merge base between 'one' and 'two'
- * @param repo the repository where the commits exist
- * @param one one of the commits
- * @param two the other commit
- * @return 0 on success, GIT_ENOTFOUND if not found or error code
- */
-GIT_EXTERN(int) git_merge_base(
- git_oid *out,
- git_repository *repo,
- const git_oid *one,
- const git_oid *two);
-
-/**
- * Find merge bases between two commits
- *
- * @param out array in which to store the resulting ids
- * @param repo the repository where the commits exist
- * @param one one of the commits
- * @param two the other commit
- * @return 0 on success, GIT_ENOTFOUND if not found or error code
- */
-GIT_EXTERN(int) git_merge_bases(
- git_oidarray *out,
- git_repository *repo,
- const git_oid *one,
- const git_oid *two);
-
-/**
- * Find a merge base given a list of commits
- *
- * @param out the OID of a merge base considering all the commits
- * @param repo the repository where the commits exist
- * @param length The number of commits in the provided `input_array`
- * @param input_array oids of the commits
- * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
- */
-GIT_EXTERN(int) git_merge_base_many(
- git_oid *out,
- git_repository *repo,
- size_t length,
- const git_oid input_array[]);
-
-/**
- * Find all merge bases given a list of commits
- *
- * @param out array in which to store the resulting ids
- * @param repo the repository where the commits exist
- * @param length The number of commits in the provided `input_array`
- * @param input_array oids of the commits
- * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
- */
-GIT_EXTERN(int) git_merge_bases_many(
- git_oidarray *out,
- git_repository *repo,
- size_t length,
- const git_oid input_array[]);
-
-/**
- * Find a merge base in preparation for an octopus merge
- *
- * @param out the OID of a merge base considering all the commits
- * @param repo the repository where the commits exist
- * @param length The number of commits in the provided `input_array`
- * @param input_array oids of the commits
- * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
- */
-GIT_EXTERN(int) git_merge_base_octopus(
- git_oid *out,
- git_repository *repo,
- size_t length,
- const git_oid input_array[]);
-
-/**
- * Merge two files as they exist in the in-memory data structures, using
- * the given common ancestor as the baseline, producing a
- * `git_merge_file_result` that reflects the merge result. The
- * `git_merge_file_result` must be freed with `git_merge_file_result_free`.
- *
- * Note that this function does not reference a repository and any
- * configuration must be passed as `git_merge_file_options`.
- *
- * @param out The git_merge_file_result to be filled in
- * @param ancestor The contents of the ancestor file
- * @param ours The contents of the file in "our" side
- * @param theirs The contents of the file in "their" side
- * @param opts The merge file options or `NULL` for defaults
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_merge_file(
- git_merge_file_result *out,
- const git_merge_file_input *ancestor,
- const git_merge_file_input *ours,
- const git_merge_file_input *theirs,
- const git_merge_file_options *opts);
-
-/**
- * Merge two files as they exist in the index, using the given common
- * ancestor as the baseline, producing a `git_merge_file_result` that
- * reflects the merge result. The `git_merge_file_result` must be freed with
- * `git_merge_file_result_free`.
- *
- * @param out The git_merge_file_result to be filled in
- * @param repo The repository
- * @param ancestor The index entry for the ancestor file (stage level 1)
- * @param ours The index entry for our file (stage level 2)
- * @param theirs The index entry for their file (stage level 3)
- * @param opts The merge file options or NULL
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_merge_file_from_index(
- git_merge_file_result *out,
- git_repository *repo,
- const git_index_entry *ancestor,
- const git_index_entry *ours,
- const git_index_entry *theirs,
- const git_merge_file_options *opts);
-
-/**
- * Frees a `git_merge_file_result`.
- *
- * @param result The result to free or `NULL`
- */
-GIT_EXTERN(void) git_merge_file_result_free(git_merge_file_result *result);
-
-/**
- * Merge two trees, producing a `git_index` that reflects the result of
- * the merge. The index may be written as-is to the working directory
- * or checked out. If the index is to be converted to a tree, the caller
- * should resolve any conflicts that arose as part of the merge.
- *
- * The returned index must be freed explicitly with `git_index_free`.
- *
- * @param out pointer to store the index result in
- * @param repo repository that contains the given trees
- * @param ancestor_tree the common ancestor between the trees (or null if none)
- * @param our_tree the tree that reflects the destination tree
- * @param their_tree the tree to merge in to `our_tree`
- * @param opts the merge tree options (or null for defaults)
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_merge_trees(
- git_index **out,
- git_repository *repo,
- const git_tree *ancestor_tree,
- const git_tree *our_tree,
- const git_tree *their_tree,
- const git_merge_options *opts);
-
-/**
- * Merge two commits, producing a `git_index` that reflects the result of
- * the merge. The index may be written as-is to the working directory
- * or checked out. If the index is to be converted to a tree, the caller
- * should resolve any conflicts that arose as part of the merge.
- *
- * The returned index must be freed explicitly with `git_index_free`.
- *
- * @param out pointer to store the index result in
- * @param repo repository that contains the given trees
- * @param our_commit the commit that reflects the destination tree
- * @param their_commit the commit to merge in to `our_commit`
- * @param opts the merge tree options (or null for defaults)
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_merge_commits(
- git_index **out,
- git_repository *repo,
- const git_commit *our_commit,
- const git_commit *their_commit,
- const git_merge_options *opts);
-
-/**
- * Merges the given commit(s) into HEAD, writing the results into the working
- * directory. Any changes are staged for commit and any conflicts are written
- * to the index. Callers should inspect the repository's index after this
- * completes, resolve any conflicts and prepare a commit.
- *
- * For compatibility with git, the repository is put into a merging
- * state. Once the commit is done (or if the uses wishes to abort),
- * you should clear this state by calling
- * `git_repository_state_cleanup()`.
- *
- * @param repo the repository to merge
- * @param their_heads the heads to merge into
- * @param their_heads_len the number of heads to merge
- * @param merge_opts merge options
- * @param checkout_opts checkout options
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_merge(
- git_repository *repo,
- const git_annotated_commit **their_heads,
- size_t their_heads_len,
- const git_merge_options *merge_opts,
- const git_checkout_options *checkout_opts);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_message_h__
-#define INCLUDE_git_message_h__
-
-#include "common.h"
-#include "buffer.h"
-
-/**
- * @file git2/message.h
- * @brief Git message management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Clean up message from excess whitespace and make sure that the last line
- * ends with a '\n'.
- *
- * Optionally, can remove lines starting with a "#".
- *
- * @param out The user-allocated git_buf which will be filled with the
- * cleaned up message.
- *
- * @param message The message to be prettified.
- *
- * @param strip_comments Non-zero to remove comment lines, 0 to leave them in.
- *
- * @param comment_char Comment character. Lines starting with this character
- * are considered to be comments and removed if `strip_comments` is non-zero.
- *
- * @return 0 or an error code.
- */
-GIT_EXTERN(int) git_message_prettify(git_buf *out, const char *message, int strip_comments, char comment_char);
-
-/** @} */
-GIT_END_DECL
-
-#endif /* INCLUDE_git_message_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_net_h__
-#define INCLUDE_git_net_h__
-
-#include "common.h"
-#include "oid.h"
-#include "types.h"
-
-/**
- * @file git2/net.h
- * @brief Git networking declarations
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-#define GIT_DEFAULT_PORT "9418"
-
-/**
- * Direction of the connection.
- *
- * We need this because we need to know whether we should call
- * git-upload-pack or git-receive-pack on the remote end when get_refs
- * gets called.
- */
-typedef enum {
- GIT_DIRECTION_FETCH = 0,
- GIT_DIRECTION_PUSH = 1
-} git_direction;
-
-/**
- * Description of a reference advertised by a remote server, given out
- * on `ls` calls.
- */
-struct git_remote_head {
- int local; /* available locally */
- git_oid oid;
- git_oid loid;
- char *name;
- /**
- * If the server send a symref mapping for this ref, this will
- * point to the target.
- */
- char *symref_target;
-};
-
-/**
- * Callback for listing the remote heads
- */
-typedef int (*git_headlist_cb)(git_remote_head *rhead, void *payload);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_note_h__
-#define INCLUDE_git_note_h__
-
-#include "oid.h"
-
-/**
- * @file git2/notes.h
- * @brief Git notes management routines
- * @defgroup git_note Git notes management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Callback for git_note_foreach.
- *
- * Receives:
- * - blob_id: Oid of the blob containing the message
- * - annotated_object_id: Oid of the git object being annotated
- * - payload: Payload data passed to `git_note_foreach`
- */
-typedef int (*git_note_foreach_cb)(
- const git_oid *blob_id, const git_oid *annotated_object_id, void *payload);
-
-/**
- * note iterator
- */
-typedef struct git_iterator git_note_iterator;
-
-/**
- * Creates a new iterator for notes
- *
- * The iterator must be freed manually by the user.
- *
- * @param out pointer to the iterator
- * @param repo repository where to look up the note
- * @param notes_ref canonical name of the reference to use (optional); defaults to
- * "refs/notes/commits"
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_note_iterator_new(
- git_note_iterator **out,
- git_repository *repo,
- const char *notes_ref);
-
-/**
- * Frees an git_note_iterator
- *
- * @param it pointer to the iterator
- */
-GIT_EXTERN(void) git_note_iterator_free(git_note_iterator *it);
-
-/**
- * Return the current item (note_id and annotated_id) and advance the iterator
- * internally to the next value
- *
- * @param note_id id of blob containing the message
- * @param annotated_id id of the git object being annotated
- * @param it pointer to the iterator
- *
- * @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code
- * (negative value)
- */
-GIT_EXTERN(int) git_note_next(
- git_oid* note_id,
- git_oid* annotated_id,
- git_note_iterator *it);
-
-
-/**
- * Read the note for an object
- *
- * The note must be freed manually by the user.
- *
- * @param out pointer to the read note; NULL in case of error
- * @param repo repository where to look up the note
- * @param notes_ref canonical name of the reference to use (optional); defaults to
- * "refs/notes/commits"
- * @param oid OID of the git object to read the note from
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_note_read(
- git_note **out,
- git_repository *repo,
- const char *notes_ref,
- const git_oid *oid);
-
-/**
- * Get the note author
- *
- * @param note the note
- * @return the author
- */
-GIT_EXTERN(const git_signature *) git_note_author(const git_note *note);
-
-/**
- * Get the note committer
- *
- * @param note the note
- * @return the committer
- */
-GIT_EXTERN(const git_signature *) git_note_committer(const git_note *note);
-
-
-/**
- * Get the note message
- *
- * @param note the note
- * @return the note message
- */
-GIT_EXTERN(const char *) git_note_message(const git_note *note);
-
-
-/**
- * Get the note object's id
- *
- * @param note the note
- * @return the note object's id
- */
-GIT_EXTERN(const git_oid *) git_note_id(const git_note *note);
-
-/**
- * Add a note for an object
- *
- * @param out pointer to store the OID (optional); NULL in case of error
- * @param repo repository where to store the note
- * @param notes_ref canonical name of the reference to use (optional);
- * defaults to "refs/notes/commits"
- * @param author signature of the notes commit author
- * @param committer signature of the notes commit committer
- * @param oid OID of the git object to decorate
- * @param note Content of the note to add for object oid
- * @param force Overwrite existing note
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_note_create(
- git_oid *out,
- git_repository *repo,
- const char *notes_ref,
- const git_signature *author,
- const git_signature *committer,
- const git_oid *oid,
- const char *note,
- int force);
-
-
-/**
- * Remove the note for an object
- *
- * @param repo repository where the note lives
- * @param notes_ref canonical name of the reference to use (optional);
- * defaults to "refs/notes/commits"
- * @param author signature of the notes commit author
- * @param committer signature of the notes commit committer
- * @param oid OID of the git object to remove the note from
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_note_remove(
- git_repository *repo,
- const char *notes_ref,
- const git_signature *author,
- const git_signature *committer,
- const git_oid *oid);
-
-/**
- * Free a git_note object
- *
- * @param note git_note object
- */
-GIT_EXTERN(void) git_note_free(git_note *note);
-
-/**
- * Get the default notes reference for a repository
- *
- * @param out buffer in which to store the name of the default notes reference
- * @param repo The Git repository
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_note_default_ref(git_buf *out, git_repository *repo);
-
-/**
- * Loop over all the notes within a specified namespace
- * and issue a callback for each one.
- *
- * @param repo Repository where to find the notes.
- *
- * @param notes_ref Reference to read from (optional); defaults to
- * "refs/notes/commits".
- *
- * @param note_cb Callback to invoke per found annotation. Return non-zero
- * to stop looping.
- *
- * @param payload Extra parameter to callback function.
- *
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_note_foreach(
- git_repository *repo,
- const char *notes_ref,
- git_note_foreach_cb note_cb,
- void *payload);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_object_h__
-#define INCLUDE_git_object_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "buffer.h"
-
-/**
- * @file git2/object.h
- * @brief Git revision object management routines
- * @defgroup git_object Git revision object management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Lookup a reference to one of the objects in a repository.
- *
- * The generated reference is owned by the repository and
- * should be closed with the `git_object_free` method
- * instead of free'd manually.
- *
- * The 'type' parameter must match the type of the object
- * in the odb; the method will fail otherwise.
- * The special value 'GIT_OBJ_ANY' may be passed to let
- * the method guess the object's type.
- *
- * @param object pointer to the looked-up object
- * @param repo the repository to look up the object
- * @param id the unique identifier for the object
- * @param type the type of the object
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_object_lookup(
- git_object **object,
- git_repository *repo,
- const git_oid *id,
- git_otype type);
-
-/**
- * Lookup a reference to one of the objects in a repository,
- * given a prefix of its identifier (short id).
- *
- * The object obtained will be so that its identifier
- * matches the first 'len' hexadecimal characters
- * (packets of 4 bits) of the given 'id'.
- * 'len' must be at least GIT_OID_MINPREFIXLEN, and
- * long enough to identify a unique object matching
- * the prefix; otherwise the method will fail.
- *
- * The generated reference is owned by the repository and
- * should be closed with the `git_object_free` method
- * instead of free'd manually.
- *
- * The 'type' parameter must match the type of the object
- * in the odb; the method will fail otherwise.
- * The special value 'GIT_OBJ_ANY' may be passed to let
- * the method guess the object's type.
- *
- * @param object_out pointer where to store the looked-up object
- * @param repo the repository to look up the object
- * @param id a short identifier for the object
- * @param len the length of the short identifier
- * @param type the type of the object
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_object_lookup_prefix(
- git_object **object_out,
- git_repository *repo,
- const git_oid *id,
- size_t len,
- git_otype type);
-
-
-/**
- * Lookup an object that represents a tree entry.
- *
- * @param out buffer that receives a pointer to the object (which must be freed
- * by the caller)
- * @param treeish root object that can be peeled to a tree
- * @param path relative path from the root object to the desired object
- * @param type type of object desired
- * @return 0 on success, or an error code
- */
-GIT_EXTERN(int) git_object_lookup_bypath(
- git_object **out,
- const git_object *treeish,
- const char *path,
- git_otype type);
-
-/**
- * Get the id (SHA1) of a repository object
- *
- * @param obj the repository object
- * @return the SHA1 id
- */
-GIT_EXTERN(const git_oid *) git_object_id(const git_object *obj);
-
-/**
- * Get a short abbreviated OID string for the object
- *
- * This starts at the "core.abbrev" length (default 7 characters) and
- * iteratively extends to a longer string if that length is ambiguous.
- * The result will be unambiguous (at least until new objects are added to
- * the repository).
- *
- * @param out Buffer to write string into
- * @param obj The object to get an ID for
- * @return 0 on success, <0 for error
- */
-GIT_EXTERN(int) git_object_short_id(git_buf *out, const git_object *obj);
-
-/**
- * Get the object type of an object
- *
- * @param obj the repository object
- * @return the object's type
- */
-GIT_EXTERN(git_otype) git_object_type(const git_object *obj);
-
-/**
- * Get the repository that owns this object
- *
- * Freeing or calling `git_repository_close` on the
- * returned pointer will invalidate the actual object.
- *
- * Any other operation may be run on the repository without
- * affecting the object.
- *
- * @param obj the object
- * @return the repository who owns this object
- */
-GIT_EXTERN(git_repository *) git_object_owner(const git_object *obj);
-
-/**
- * Close an open object
- *
- * This method instructs the library to close an existing
- * object; note that git_objects are owned and cached by the repository
- * so the object may or may not be freed after this library call,
- * depending on how aggressive is the caching mechanism used
- * by the repository.
- *
- * IMPORTANT:
- * It *is* necessary to call this method when you stop using
- * an object. Failure to do so will cause a memory leak.
- *
- * @param object the object to close
- */
-GIT_EXTERN(void) git_object_free(git_object *object);
-
-/**
- * Convert an object type to its string representation.
- *
- * The result is a pointer to a string in static memory and
- * should not be free()'ed.
- *
- * @param type object type to convert.
- * @return the corresponding string representation.
- */
-GIT_EXTERN(const char *) git_object_type2string(git_otype type);
-
-/**
- * Convert a string object type representation to it's git_otype.
- *
- * @param str the string to convert.
- * @return the corresponding git_otype.
- */
-GIT_EXTERN(git_otype) git_object_string2type(const char *str);
-
-/**
- * Determine if the given git_otype is a valid loose object type.
- *
- * @param type object type to test.
- * @return true if the type represents a valid loose object type,
- * false otherwise.
- */
-GIT_EXTERN(int) git_object_typeisloose(git_otype type);
-
-/**
- * Get the size in bytes for the structure which
- * acts as an in-memory representation of any given
- * object type.
- *
- * For all the core types, this would the equivalent
- * of calling `sizeof(git_commit)` if the core types
- * were not opaque on the external API.
- *
- * @param type object type to get its size
- * @return size in bytes of the object
- */
-GIT_EXTERN(size_t) git_object__size(git_otype type);
-
-/**
- * Recursively peel an object until an object of the specified type is met.
- *
- * If the query cannot be satisfied due to the object model,
- * GIT_EINVALIDSPEC will be returned (e.g. trying to peel a blob to a
- * tree).
- *
- * If you pass `GIT_OBJ_ANY` as the target type, then the object will
- * be peeled until the type changes. A tag will be peeled until the
- * referenced object is no longer a tag, and a commit will be peeled
- * to a tree. Any other object type will return GIT_EINVALIDSPEC.
- *
- * If peeling a tag we discover an object which cannot be peeled to
- * the target type due to the object model, GIT_EPEEL will be
- * returned.
- *
- * You must free the returned object.
- *
- * @param peeled Pointer to the peeled git_object
- * @param object The object to be processed
- * @param target_type The type of the requested object (a GIT_OBJ_ value)
- * @return 0 on success, GIT_EINVALIDSPEC, GIT_EPEEL, or an error code
- */
-GIT_EXTERN(int) git_object_peel(
- git_object **peeled,
- const git_object *object,
- git_otype target_type);
-
-/**
- * Create an in-memory copy of a Git object. The copy must be
- * explicitly free'd or it will leak.
- *
- * @param dest Pointer to store the copy of the object
- * @param source Original object to copy
- */
-GIT_EXTERN(int) git_object_dup(git_object **dest, git_object *source);
-
-/** @} */
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_odb_h__
-#define INCLUDE_git_odb_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "oidarray.h"
-
-/**
- * @file git2/odb.h
- * @brief Git object database routines
- * @defgroup git_odb Git object database routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Function type for callbacks from git_odb_foreach.
- */
-typedef int (*git_odb_foreach_cb)(const git_oid *id, void *payload);
-
-/**
- * Create a new object database with no backends.
- *
- * Before the ODB can be used for read/writing, a custom database
- * backend must be manually added using `git_odb_add_backend()`
- *
- * @param out location to store the database pointer, if opened.
- * Set to NULL if the open failed.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_odb_new(git_odb **out);
-
-/**
- * Create a new object database and automatically add
- * the two default backends:
- *
- * - git_odb_backend_loose: read and write loose object files
- * from disk, assuming `objects_dir` as the Objects folder
- *
- * - git_odb_backend_pack: read objects from packfiles,
- * assuming `objects_dir` as the Objects folder which
- * contains a 'pack/' folder with the corresponding data
- *
- * @param out location to store the database pointer, if opened.
- * Set to NULL if the open failed.
- * @param objects_dir path of the backends' "objects" directory.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir);
-
-/**
- * Add an on-disk alternate to an existing Object DB.
- *
- * Note that the added path must point to an `objects`, not
- * to a full repository, to use it as an alternate store.
- *
- * Alternate backends are always checked for objects *after*
- * all the main backends have been exhausted.
- *
- * Writing is disabled on alternate backends.
- *
- * @param odb database to add the backend to
- * @param path path to the objects folder for the alternate
- * @return 0 on success; error code otherwise
- */
-GIT_EXTERN(int) git_odb_add_disk_alternate(git_odb *odb, const char *path);
-
-/**
- * Close an open object database.
- *
- * @param db database pointer to close. If NULL no action is taken.
- */
-GIT_EXTERN(void) git_odb_free(git_odb *db);
-
-/**
- * Read an object from the database.
- *
- * This method queries all available ODB backends
- * trying to read the given OID.
- *
- * The returned object is reference counted and
- * internally cached, so it should be closed
- * by the user once it's no longer in use.
- *
- * @param out pointer where to store the read object
- * @param db database to search for the object in.
- * @param id identity of the object to read.
- * @return
- * - 0 if the object was read;
- * - GIT_ENOTFOUND if the object is not in the database.
- */
-GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id);
-
-/**
- * Read an object from the database, given a prefix
- * of its identifier.
- *
- * This method queries all available ODB backends
- * trying to match the 'len' first hexadecimal
- * characters of the 'short_id'.
- * The remaining (GIT_OID_HEXSZ-len)*4 bits of
- * 'short_id' must be 0s.
- * 'len' must be at least GIT_OID_MINPREFIXLEN,
- * and the prefix must be long enough to identify
- * a unique object in all the backends; the
- * method will fail otherwise.
- *
- * The returned object is reference counted and
- * internally cached, so it should be closed
- * by the user once it's no longer in use.
- *
- * @param out pointer where to store the read object
- * @param db database to search for the object in.
- * @param short_id a prefix of the id of the object to read.
- * @param len the length of the prefix
- * @return
- * - 0 if the object was read;
- * - GIT_ENOTFOUND if the object is not in the database.
- * - GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix)
- */
-GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len);
-
-/**
- * Read the header of an object from the database, without
- * reading its full contents.
- *
- * The header includes the length and the type of an object.
- *
- * Note that most backends do not support reading only the header
- * of an object, so the whole object will be read and then the
- * header will be returned.
- *
- * @param len_out pointer where to store the length
- * @param type_out pointer where to store the type
- * @param db database to search for the object in.
- * @param id identity of the object to read.
- * @return
- * - 0 if the object was read;
- * - GIT_ENOTFOUND if the object is not in the database.
- */
-GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_odb *db, const git_oid *id);
-
-/**
- * Determine if the given object can be found in the object database.
- *
- * @param db database to be searched for the given object.
- * @param id the object to search for.
- * @return
- * - 1, if the object was found
- * - 0, otherwise
- */
-GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
-
-/**
- * Determine if an object can be found in the object database by an
- * abbreviated object ID.
- *
- * @param out The full OID of the found object if just one is found.
- * @param db The database to be searched for the given object.
- * @param short_id A prefix of the id of the object to read.
- * @param len The length of the prefix.
- * @return 0 if found, GIT_ENOTFOUND if not found, GIT_EAMBIGUOUS if multiple
- * matches were found, other value < 0 if there was a read error.
- */
-GIT_EXTERN(int) git_odb_exists_prefix(
- git_oid *out, git_odb *db, const git_oid *short_id, size_t len);
-
-/**
- * The information about object IDs to query in `git_odb_expand_ids`,
- * which will be populated upon return.
- */
-typedef struct git_odb_expand_id {
- /** The object ID to expand */
- git_oid id;
-
- /**
- * The length of the object ID (in nibbles, or packets of 4 bits; the
- * number of hex characters)
- * */
- unsigned short length;
-
- /**
- * The (optional) type of the object to search for; leave as `0` or set
- * to `GIT_OBJ_ANY` to query for any object matching the ID.
- */
- git_otype type;
-} git_odb_expand_id;
-
-/**
- * Determine if one or more objects can be found in the object database
- * by their abbreviated object ID and type. The given array will be
- * updated in place: for each abbreviated ID that is unique in the
- * database, and of the given type (if specified), the full object ID,
- * object ID length (`GIT_OID_HEXSZ`) and type will be written back to
- * the array. For IDs that are not found (or are ambiguous), the
- * array entry will be zeroed.
- *
- * Note that since this function operates on multiple objects, the
- * underlying database will not be asked to be reloaded if an object is
- * not found (which is unlike other object database operations.)
- *
- * @param db The database to be searched for the given objects.
- * @param ids An array of short object IDs to search for
- * @param count The length of the `ids` array
- * @return 0 on success or an error code on failure
- */
-GIT_EXTERN(int) git_odb_expand_ids(
- git_odb *db,
- git_odb_expand_id *ids,
- size_t count);
-
-/**
- * Refresh the object database to load newly added files.
- *
- * If the object databases have changed on disk while the library
- * is running, this function will force a reload of the underlying
- * indexes.
- *
- * Use this function when you're confident that an external
- * application has tampered with the ODB.
- *
- * NOTE that it is not necessary to call this function at all. The
- * library will automatically attempt to refresh the ODB
- * when a lookup fails, to see if the looked up object exists
- * on disk but hasn't been loaded yet.
- *
- * @param db database to refresh
- * @return 0 on success, error code otherwise
- */
-GIT_EXTERN(int) git_odb_refresh(struct git_odb *db);
-
-/**
- * List all objects available in the database
- *
- * The callback will be called for each object available in the
- * database. Note that the objects are likely to be returned in the index
- * order, which would make accessing the objects in that order inefficient.
- * Return a non-zero value from the callback to stop looping.
- *
- * @param db database to use
- * @param cb the callback to call for each object
- * @param payload data to pass to the callback
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload);
-
-/**
- * Write an object directly into the ODB
- *
- * This method writes a full object straight into the ODB.
- * For most cases, it is preferred to write objects through a write
- * stream, which is both faster and less memory intensive, specially
- * for big objects.
- *
- * This method is provided for compatibility with custom backends
- * which are not able to support streaming writes
- *
- * @param out pointer to store the OID result of the write
- * @param odb object database where to store the object
- * @param data buffer with the data to store
- * @param len size of the buffer
- * @param type type of the data to store
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_odb_write(git_oid *out, git_odb *odb, const void *data, size_t len, git_otype type);
-
-/**
- * Open a stream to write an object into the ODB
- *
- * The type and final length of the object must be specified
- * when opening the stream.
- *
- * The returned stream will be of type `GIT_STREAM_WRONLY`, and it
- * won't be effective until `git_odb_stream_finalize_write` is called
- * and returns without an error
- *
- * The stream must always be freed when done with `git_odb_stream_free` or
- * will leak memory.
- *
- * @see git_odb_stream
- *
- * @param out pointer where to store the stream
- * @param db object database where the stream will write
- * @param size final size of the object that will be written
- * @param type type of the object that will be written
- * @return 0 if the stream was created; error code otherwise
- */
-GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **out, git_odb *db, git_off_t size, git_otype type);
-
-/**
- * Write to an odb stream
- *
- * This method will fail if the total number of received bytes exceeds the
- * size declared with `git_odb_open_wstream()`
- *
- * @param stream the stream
- * @param buffer the data to write
- * @param len the buffer's length
- * @return 0 if the write succeeded; error code otherwise
- */
-GIT_EXTERN(int) git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len);
-
-/**
- * Finish writing to an odb stream
- *
- * The object will take its final name and will be available to the
- * odb.
- *
- * This method will fail if the total number of received bytes
- * differs from the size declared with `git_odb_open_wstream()`
- *
- * @param out pointer to store the resulting object's id
- * @param stream the stream
- * @return 0 on success; an error code otherwise
- */
-GIT_EXTERN(int) git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream);
-
-/**
- * Read from an odb stream
- *
- * Most backends don't implement streaming reads
- */
-GIT_EXTERN(int) git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len);
-
-/**
- * Free an odb stream
- *
- * @param stream the stream to free
- */
-GIT_EXTERN(void) git_odb_stream_free(git_odb_stream *stream);
-
-/**
- * Open a stream to read an object from the ODB
- *
- * Note that most backends do *not* support streaming reads
- * because they store their objects as compressed/delta'ed blobs.
- *
- * It's recommended to use `git_odb_read` instead, which is
- * assured to work on all backends.
- *
- * The returned stream will be of type `GIT_STREAM_RDONLY` and
- * will have the following methods:
- *
- * - stream->read: read `n` bytes from the stream
- * - stream->free: free the stream
- *
- * The stream must always be free'd or will leak memory.
- *
- * @see git_odb_stream
- *
- * @param out pointer where to store the stream
- * @param db object database where the stream will read from
- * @param oid oid of the object the stream will read from
- * @return 0 if the stream was created; error code otherwise
- */
-GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **out, git_odb *db, const git_oid *oid);
-
-/**
- * Open a stream for writing a pack file to the ODB.
- *
- * If the ODB layer understands pack files, then the given
- * packfile will likely be streamed directly to disk (and a
- * corresponding index created). If the ODB layer does not
- * understand pack files, the objects will be stored in whatever
- * format the ODB layer uses.
- *
- * @see git_odb_writepack
- *
- * @param out pointer to the writepack functions
- * @param db object database where the stream will read from
- * @param progress_cb function to call with progress information.
- * Be aware that this is called inline with network and indexing operations,
- * so performance may be affected.
- * @param progress_payload payload for the progress callback
- */
-GIT_EXTERN(int) git_odb_write_pack(
- git_odb_writepack **out,
- git_odb *db,
- git_transfer_progress_cb progress_cb,
- void *progress_payload);
-
-/**
- * Determine the object-ID (sha1 hash) of a data buffer
- *
- * The resulting SHA-1 OID will be the identifier for the data
- * buffer as if the data buffer it were to written to the ODB.
- *
- * @param out the resulting object-ID.
- * @param data data to hash
- * @param len size of the data
- * @param type of the data to hash
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_odb_hash(git_oid *out, const void *data, size_t len, git_otype type);
-
-/**
- * Read a file from disk and fill a git_oid with the object id
- * that the file would have if it were written to the Object
- * Database as an object of the given type (w/o applying filters).
- * Similar functionality to git.git's `git hash-object` without
- * the `-w` flag, however, with the --no-filters flag.
- * If you need filters, see git_repository_hashfile.
- *
- * @param out oid structure the result is written into.
- * @param path file to read and determine object id for
- * @param type the type of the object that will be hashed
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_odb_hashfile(git_oid *out, const char *path, git_otype type);
-
-/**
- * Create a copy of an odb_object
- *
- * The returned copy must be manually freed with `git_odb_object_free`.
- * Note that because of an implementation detail, the returned copy will be
- * the same pointer as `source`: the object is internally refcounted, so the
- * copy still needs to be freed twice.
- *
- * @param dest pointer where to store the copy
- * @param source object to copy
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_odb_object_dup(git_odb_object **dest, git_odb_object *source);
-
-/**
- * Close an ODB object
- *
- * This method must always be called once a `git_odb_object` is no
- * longer needed, otherwise memory will leak.
- *
- * @param object object to close
- */
-GIT_EXTERN(void) git_odb_object_free(git_odb_object *object);
-
-/**
- * Return the OID of an ODB object
- *
- * This is the OID from which the object was read from
- *
- * @param object the object
- * @return a pointer to the OID
- */
-GIT_EXTERN(const git_oid *) git_odb_object_id(git_odb_object *object);
-
-/**
- * Return the data of an ODB object
- *
- * This is the uncompressed, raw data as read from the ODB,
- * without the leading header.
- *
- * This pointer is owned by the object and shall not be free'd.
- *
- * @param object the object
- * @return a pointer to the data
- */
-GIT_EXTERN(const void *) git_odb_object_data(git_odb_object *object);
-
-/**
- * Return the size of an ODB object
- *
- * This is the real size of the `data` buffer, not the
- * actual size of the object.
- *
- * @param object the object
- * @return the size
- */
-GIT_EXTERN(size_t) git_odb_object_size(git_odb_object *object);
-
-/**
- * Return the type of an ODB object
- *
- * @param object the object
- * @return the type
- */
-GIT_EXTERN(git_otype) git_odb_object_type(git_odb_object *object);
-
-/**
- * Add a custom backend to an existing Object DB
- *
- * The backends are checked in relative ordering, based on the
- * value of the `priority` parameter.
- *
- * Read <odb_backends.h> for more information.
- *
- * @param odb database to add the backend to
- * @param backend pointer to a git_odb_backend instance
- * @param priority Value for ordering the backends queue
- * @return 0 on success; error code otherwise
- */
-GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority);
-
-/**
- * Add a custom backend to an existing Object DB; this
- * backend will work as an alternate.
- *
- * Alternate backends are always checked for objects *after*
- * all the main backends have been exhausted.
- *
- * The backends are checked in relative ordering, based on the
- * value of the `priority` parameter.
- *
- * Writing is disabled on alternate backends.
- *
- * Read <odb_backends.h> for more information.
- *
- * @param odb database to add the backend to
- * @param backend pointer to a git_odb_backend instance
- * @param priority Value for ordering the backends queue
- * @return 0 on success; error code otherwise
- */
-GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority);
-
-/**
- * Get the number of ODB backend objects
- *
- * @param odb object database
- * @return number of backends in the ODB
- */
-GIT_EXTERN(size_t) git_odb_num_backends(git_odb *odb);
-
-/**
- * Lookup an ODB backend object by index
- *
- * @param out output pointer to ODB backend at pos
- * @param odb object database
- * @param pos index into object database backend list
- * @return 0 on success; GIT_ENOTFOUND if pos is invalid; other errors < 0
- */
-GIT_EXTERN(int) git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_odb_backend_h__
-#define INCLUDE_git_odb_backend_h__
-
-#include "common.h"
-#include "types.h"
-
-/**
- * @file git2/backend.h
- * @brief Git custom backend functions
- * @defgroup git_odb Git object database routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/*
- * Constructors for in-box ODB backends.
- */
-
-/**
- * Create a backend for the packfiles.
- *
- * @param out location to store the odb backend pointer
- * @param objects_dir the Git repository's objects directory
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir);
-
-/**
- * Create a backend for loose objects
- *
- * @param out location to store the odb backend pointer
- * @param objects_dir the Git repository's objects directory
- * @param compression_level zlib compression level to use
- * @param do_fsync whether to do an fsync() after writing (currently ignored)
- * @param dir_mode permissions to use creating a directory or 0 for defaults
- * @param file_mode permissions to use creating a file or 0 for defaults
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_odb_backend_loose(
- git_odb_backend **out,
- const char *objects_dir,
- int compression_level,
- int do_fsync,
- unsigned int dir_mode,
- unsigned int file_mode);
-
-/**
- * Create a backend out of a single packfile
- *
- * This can be useful for inspecting the contents of a single
- * packfile.
- *
- * @param out location to store the odb backend pointer
- * @param index_file path to the packfile's .idx file
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file);
-
-/** Streaming mode */
-typedef enum {
- GIT_STREAM_RDONLY = (1 << 1),
- GIT_STREAM_WRONLY = (1 << 2),
- GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
-} git_odb_stream_t;
-
-/**
- * A stream to read/write from a backend.
- *
- * This represents a stream of data being written to or read from a
- * backend. When writing, the frontend functions take care of
- * calculating the object's id and all `finalize_write` needs to do is
- * store the object with the id it is passed.
- */
-struct git_odb_stream {
- git_odb_backend *backend;
- unsigned int mode;
- void *hash_ctx;
-
- git_off_t declared_size;
- git_off_t received_bytes;
-
- /**
- * Write at most `len` bytes into `buffer` and advance the stream.
- */
- int (*read)(git_odb_stream *stream, char *buffer, size_t len);
-
- /**
- * Write `len` bytes from `buffer` into the stream.
- */
- int (*write)(git_odb_stream *stream, const char *buffer, size_t len);
-
- /**
- * Store the contents of the stream as an object with the id
- * specified in `oid`.
- *
- * This method might not be invoked if:
- * - an error occurs earlier with the `write` callback,
- * - the object referred to by `oid` already exists in any backend, or
- * - the final number of received bytes differs from the size declared
- * with `git_odb_open_wstream()`
- */
- int (*finalize_write)(git_odb_stream *stream, const git_oid *oid);
-
- /**
- * Free the stream's memory.
- *
- * This method might be called without a call to `finalize_write` if
- * an error occurs or if the object is already present in the ODB.
- */
- void (*free)(git_odb_stream *stream);
-};
-
-/** A stream to write a pack file to the ODB */
-struct git_odb_writepack {
- git_odb_backend *backend;
-
- int (*append)(git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
- int (*commit)(git_odb_writepack *writepack, git_transfer_progress *stats);
- void (*free)(git_odb_writepack *writepack);
-};
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_oid_h__
-#define INCLUDE_git_oid_h__
-
-#include "common.h"
-#include "types.h"
-
-/**
- * @file git2/oid.h
- * @brief Git object id routines
- * @defgroup git_oid Git object id routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/** Size (in bytes) of a raw/binary oid */
-#define GIT_OID_RAWSZ 20
-
-/** Size (in bytes) of a hex formatted oid */
-#define GIT_OID_HEXSZ (GIT_OID_RAWSZ * 2)
-
-/** Minimum length (in number of hex characters,
- * i.e. packets of 4 bits) of an oid prefix */
-#define GIT_OID_MINPREFIXLEN 4
-
-/** Unique identity of any object (commit, tree, blob, tag). */
-typedef struct git_oid {
- /** raw binary formatted id */
- unsigned char id[GIT_OID_RAWSZ];
-} git_oid;
-
-/**
- * Parse a hex formatted object id into a git_oid.
- *
- * @param out oid structure the result is written into.
- * @param str input hex string; must be pointing at the start of
- * the hex sequence and have at least the number of bytes
- * needed for an oid encoded in hex (40 bytes).
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str);
-
-/**
- * Parse a hex formatted null-terminated string into a git_oid.
- *
- * @param out oid structure the result is written into.
- * @param str input hex string; must be at least 4 characters
- * long and null-terminated.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_oid_fromstrp(git_oid *out, const char *str);
-
-/**
- * Parse N characters of a hex formatted object id into a git_oid
- *
- * If N is odd, N-1 characters will be parsed instead.
- * The remaining space in the git_oid will be set to zero.
- *
- * @param out oid structure the result is written into.
- * @param str input hex string of at least size `length`
- * @param length length of the input string
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_oid_fromstrn(git_oid *out, const char *str, size_t length);
-
-/**
- * Copy an already raw oid into a git_oid structure.
- *
- * @param out oid structure the result is written into.
- * @param raw the raw input bytes to be copied.
- */
-GIT_EXTERN(void) git_oid_fromraw(git_oid *out, const unsigned char *raw);
-
-/**
- * Format a git_oid into a hex string.
- *
- * @param out output hex string; must be pointing at the start of
- * the hex sequence and have at least the number of bytes
- * needed for an oid encoded in hex (40 bytes). Only the
- * oid digits are written; a '\\0' terminator must be added
- * by the caller if it is required.
- * @param id oid structure to format.
- */
-GIT_EXTERN(void) git_oid_fmt(char *out, const git_oid *id);
-
-/**
- * Format a git_oid into a partial hex string.
- *
- * @param out output hex string; you say how many bytes to write.
- * If the number of bytes is > GIT_OID_HEXSZ, extra bytes
- * will be zeroed; if not, a '\0' terminator is NOT added.
- * @param n number of characters to write into out string
- * @param id oid structure to format.
- */
-GIT_EXTERN(void) git_oid_nfmt(char *out, size_t n, const git_oid *id);
-
-/**
- * Format a git_oid into a loose-object path string.
- *
- * The resulting string is "aa/...", where "aa" is the first two
- * hex digits of the oid and "..." is the remaining 38 digits.
- *
- * @param out output hex string; must be pointing at the start of
- * the hex sequence and have at least the number of bytes
- * needed for an oid encoded in hex (41 bytes). Only the
- * oid digits are written; a '\\0' terminator must be added
- * by the caller if it is required.
- * @param id oid structure to format.
- */
-GIT_EXTERN(void) git_oid_pathfmt(char *out, const git_oid *id);
-
-/**
- * Format a git_oid into a statically allocated c-string.
- *
- * The c-string is owned by the library and should not be freed
- * by the user. If libgit2 is built with thread support, the string
- * will be stored in TLS (i.e. one buffer per thread) to allow for
- * concurrent calls of the function.
- *
- * @param oid The oid structure to format
- * @return the c-string
- */
-GIT_EXTERN(char *) git_oid_tostr_s(const git_oid *oid);
-
-/**
- * Format a git_oid into a buffer as a hex format c-string.
- *
- * If the buffer is smaller than GIT_OID_HEXSZ+1, then the resulting
- * oid c-string will be truncated to n-1 characters (but will still be
- * NUL-byte terminated).
- *
- * If there are any input parameter errors (out == NULL, n == 0, oid ==
- * NULL), then a pointer to an empty string is returned, so that the
- * return value can always be printed.
- *
- * @param out the buffer into which the oid string is output.
- * @param n the size of the out buffer.
- * @param id the oid structure to format.
- * @return the out buffer pointer, assuming no input parameter
- * errors, otherwise a pointer to an empty string.
- */
-GIT_EXTERN(char *) git_oid_tostr(char *out, size_t n, const git_oid *id);
-
-/**
- * Copy an oid from one structure to another.
- *
- * @param out oid structure the result is written into.
- * @param src oid structure to copy from.
- */
-GIT_EXTERN(void) git_oid_cpy(git_oid *out, const git_oid *src);
-
-/**
- * Compare two oid structures.
- *
- * @param a first oid structure.
- * @param b second oid structure.
- * @return <0, 0, >0 if a < b, a == b, a > b.
- */
-GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b);
-
-/**
- * Compare two oid structures for equality
- *
- * @param a first oid structure.
- * @param b second oid structure.
- * @return true if equal, false otherwise
- */
-GIT_EXTERN(int) git_oid_equal(const git_oid *a, const git_oid *b);
-
-/**
- * Compare the first 'len' hexadecimal characters (packets of 4 bits)
- * of two oid structures.
- *
- * @param a first oid structure.
- * @param b second oid structure.
- * @param len the number of hex chars to compare
- * @return 0 in case of a match
- */
-GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, size_t len);
-
-/**
- * Check if an oid equals an hex formatted object id.
- *
- * @param id oid structure.
- * @param str input hex string of an object id.
- * @return 0 in case of a match, -1 otherwise.
- */
-GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str);
-
-/**
- * Compare an oid to an hex formatted object id.
- *
- * @param id oid structure.
- * @param str input hex string of an object id.
- * @return -1 if str is not valid, <0 if id sorts before str,
- * 0 if id matches str, >0 if id sorts after str.
- */
-GIT_EXTERN(int) git_oid_strcmp(const git_oid *id, const char *str);
-
-/**
- * Check is an oid is all zeros.
- *
- * @return 1 if all zeros, 0 otherwise.
- */
-GIT_EXTERN(int) git_oid_iszero(const git_oid *id);
-
-/**
- * OID Shortener object
- */
-typedef struct git_oid_shorten git_oid_shorten;
-
-/**
- * Create a new OID shortener.
- *
- * The OID shortener is used to process a list of OIDs
- * in text form and return the shortest length that would
- * uniquely identify all of them.
- *
- * E.g. look at the result of `git log --abbrev`.
- *
- * @param min_length The minimal length for all identifiers,
- * which will be used even if shorter OIDs would still
- * be unique.
- * @return a `git_oid_shorten` instance, NULL if OOM
- */
-GIT_EXTERN(git_oid_shorten *) git_oid_shorten_new(size_t min_length);
-
-/**
- * Add a new OID to set of shortened OIDs and calculate
- * the minimal length to uniquely identify all the OIDs in
- * the set.
- *
- * The OID is expected to be a 40-char hexadecimal string.
- * The OID is owned by the user and will not be modified
- * or freed.
- *
- * For performance reasons, there is a hard-limit of how many
- * OIDs can be added to a single set (around ~32000, assuming
- * a mostly randomized distribution), which should be enough
- * for any kind of program, and keeps the algorithm fast and
- * memory-efficient.
- *
- * Attempting to add more than those OIDs will result in a
- * GITERR_INVALID error
- *
- * @param os a `git_oid_shorten` instance
- * @param text_id an OID in text form
- * @return the minimal length to uniquely identify all OIDs
- * added so far to the set; or an error code (<0) if an
- * error occurs.
- */
-GIT_EXTERN(int) git_oid_shorten_add(git_oid_shorten *os, const char *text_id);
-
-/**
- * Free an OID shortener instance
- *
- * @param os a `git_oid_shorten` instance
- */
-GIT_EXTERN(void) git_oid_shorten_free(git_oid_shorten *os);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_oidarray_h__
-#define INCLUDE_git_oidarray_h__
-
-#include "common.h"
-#include "oid.h"
-
-GIT_BEGIN_DECL
-
-/** Array of object ids */
-typedef struct git_oidarray {
- git_oid *ids;
- size_t count;
-} git_oidarray;
-
-/**
- * Free the OID array
- *
- * This method must (and must only) be called on `git_oidarray`
- * objects where the array is allocated by the library. Not doing so,
- * will result in a memory leak.
- *
- * This does not free the `git_oidarray` itself, since the library will
- * never allocate that object directly itself (it is more commonly embedded
- * inside another struct or created on the stack).
- *
- * @param array git_oidarray from which to free oid data
- */
-GIT_EXTERN(void) git_oidarray_free(git_oidarray *array);
-
-/** @} */
-GIT_END_DECL
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_pack_h__
-#define INCLUDE_git_pack_h__
-
-#include "common.h"
-#include "oid.h"
-
-/**
- * @file git2/pack.h
- * @brief Git pack management routines
- *
- * Packing objects
- * ---------------
- *
- * Creation of packfiles requires two steps:
- *
- * - First, insert all the objects you want to put into the packfile
- * using `git_packbuilder_insert` and `git_packbuilder_insert_tree`.
- * It's important to add the objects in recency order ("in the order
- * that they are 'reachable' from head").
- *
- * "ANY order will give you a working pack, ... [but it is] the thing
- * that gives packs good locality. It keeps the objects close to the
- * head (whether they are old or new, but they are _reachable_ from the
- * head) at the head of the pack. So packs actually have absolutely
- * _wonderful_ IO patterns." - Linus Torvalds
- * git.git/Documentation/technical/pack-heuristics.txt
- *
- * - Second, use `git_packbuilder_write` or `git_packbuilder_foreach` to
- * write the resulting packfile.
- *
- * libgit2 will take care of the delta ordering and generation.
- * `git_packbuilder_set_threads` can be used to adjust the number of
- * threads used for the process.
- *
- * See tests/pack/packbuilder.c for an example.
- *
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Stages that are reported by the packbuilder progress callback.
- */
-typedef enum {
- GIT_PACKBUILDER_ADDING_OBJECTS = 0,
- GIT_PACKBUILDER_DELTAFICATION = 1,
-} git_packbuilder_stage_t;
-
-/**
- * Initialize a new packbuilder
- *
- * @param out The new packbuilder object
- * @param repo The repository
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_packbuilder_new(git_packbuilder **out, git_repository *repo);
-
-/**
- * Set number of threads to spawn
- *
- * By default, libgit2 won't spawn any threads at all;
- * when set to 0, libgit2 will autodetect the number of
- * CPUs.
- *
- * @param pb The packbuilder
- * @param n Number of threads to spawn
- * @return number of actual threads to be used
- */
-GIT_EXTERN(unsigned int) git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n);
-
-/**
- * Insert a single object
- *
- * For an optimal pack it's mandatory to insert objects in recency order,
- * commits followed by trees and blobs.
- *
- * @param pb The packbuilder
- * @param id The oid of the commit
- * @param name The name; might be NULL
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_packbuilder_insert(git_packbuilder *pb, const git_oid *id, const char *name);
-
-/**
- * Insert a root tree object
- *
- * This will add the tree as well as all referenced trees and blobs.
- *
- * @param pb The packbuilder
- * @param id The oid of the root tree
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *id);
-
-/**
- * Insert a commit object
- *
- * This will add a commit as well as the completed referenced tree.
- *
- * @param pb The packbuilder
- * @param id The oid of the commit
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *id);
-
-/**
- * Insert objects as given by the walk
- *
- * Those commits and all objects they reference will be inserted into
- * the packbuilder.
- *
- * @param pb the packbuilder
- * @param walk the revwalk to use to fill the packbuilder
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_packbuilder_insert_walk(git_packbuilder *pb, git_revwalk *walk);
-
-/**
- * Recursively insert an object and its referenced objects
- *
- * Insert the object as well as any object it references.
- *
- * @param pb the packbuilder
- * @param id the id of the root object to insert
- * @param name optional name for the object
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_packbuilder_insert_recur(git_packbuilder *pb, const git_oid *id, const char *name);
-
-/**
- * Write the contents of the packfile to an in-memory buffer
- *
- * The contents of the buffer will become a valid packfile, even though there
- * will be no attached index
- *
- * @param buf Buffer where to write the packfile
- * @param pb The packbuilder
- */
-GIT_EXTERN(int) git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb);
-
-/**
- * Write the new pack and corresponding index file to path.
- *
- * @param pb The packbuilder
- * @param path to the directory where the packfile and index should be stored
- * @param mode permissions to use creating a packfile or 0 for defaults
- * @param progress_cb function to call with progress information from the indexer (optional)
- * @param progress_cb_payload payload for the progress callback (optional)
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_packbuilder_write(
- git_packbuilder *pb,
- const char *path,
- unsigned int mode,
- git_transfer_progress_cb progress_cb,
- void *progress_cb_payload);
-
-/**
-* Get the packfile's hash
-*
-* A packfile's name is derived from the sorted hashing of all object
-* names. This is only correct after the packfile has been written.
-*
-* @param pb The packbuilder object
-*/
-GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb);
-
-typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload);
-
-/**
- * Create the new pack and pass each object to the callback
- *
- * @param pb the packbuilder
- * @param cb the callback to call with each packed object's buffer
- * @param payload the callback's data
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_packbuilder_foreach(git_packbuilder *pb, git_packbuilder_foreach_cb cb, void *payload);
-
-/**
- * Get the total number of objects the packbuilder will write out
- *
- * @param pb the packbuilder
- * @return the number of objects in the packfile
- */
-GIT_EXTERN(size_t) git_packbuilder_object_count(git_packbuilder *pb);
-
-/**
- * Get the number of objects the packbuilder has already written out
- *
- * @param pb the packbuilder
- * @return the number of objects which have already been written
- */
-GIT_EXTERN(size_t) git_packbuilder_written(git_packbuilder *pb);
-
-/** Packbuilder progress notification function */
-typedef int (*git_packbuilder_progress)(
- int stage,
- uint32_t current,
- uint32_t total,
- void *payload);
-
-/**
- * Set the callbacks for a packbuilder
- *
- * @param pb The packbuilder object
- * @param progress_cb Function to call with progress information during
- * pack building. Be aware that this is called inline with pack building
- * operations, so performance may be affected.
- * @param progress_cb_payload Payload for progress callback.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_packbuilder_set_callbacks(
- git_packbuilder *pb,
- git_packbuilder_progress progress_cb,
- void *progress_cb_payload);
-
-/**
- * Free the packbuilder and all associated data
- *
- * @param pb The packbuilder
- */
-GIT_EXTERN(void) git_packbuilder_free(git_packbuilder *pb);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_patch_h__
-#define INCLUDE_git_patch_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "diff.h"
-
-/**
- * @file git2/patch.h
- * @brief Patch handling routines.
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * The diff patch is used to store all the text diffs for a delta.
- *
- * You can easily loop over the content of patches and get information about
- * them.
- */
-typedef struct git_patch git_patch;
-
-/**
- * Return a patch for an entry in the diff list.
- *
- * The `git_patch` is a newly created object contains the text diffs
- * for the delta. You have to call `git_patch_free()` when you are
- * done with it. You can use the patch object to loop over all the hunks
- * and lines in the diff of the one delta.
- *
- * For an unchanged file or a binary file, no `git_patch` will be
- * created, the output will be set to NULL, and the `binary` flag will be
- * set true in the `git_diff_delta` structure.
- *
- * It is okay to pass NULL for either of the output parameters; if you pass
- * NULL for the `git_patch`, then the text diff will not be calculated.
- *
- * @param out Output parameter for the delta patch object
- * @param diff Diff list object
- * @param idx Index into diff list
- * @return 0 on success, other value < 0 on error
- */
-GIT_EXTERN(int) git_patch_from_diff(
- git_patch **out, git_diff *diff, size_t idx);
-
-/**
- * Directly generate a patch from the difference between two blobs.
- *
- * This is just like `git_diff_blobs()` except it generates a patch object
- * for the difference instead of directly making callbacks. You can use the
- * standard `git_patch` accessor functions to read the patch data, and
- * you must call `git_patch_free()` on the patch when done.
- *
- * @param out The generated patch; NULL on error
- * @param old_blob Blob for old side of diff, or NULL for empty blob
- * @param old_as_path Treat old blob as if it had this filename; can be NULL
- * @param new_blob Blob for new side of diff, or NULL for empty blob
- * @param new_as_path Treat new blob as if it had this filename; can be NULL
- * @param opts Options for diff, or NULL for default options
- * @return 0 on success or error code < 0
- */
-GIT_EXTERN(int) git_patch_from_blobs(
- git_patch **out,
- const git_blob *old_blob,
- const char *old_as_path,
- const git_blob *new_blob,
- const char *new_as_path,
- const git_diff_options *opts);
-
-/**
- * Directly generate a patch from the difference between a blob and a buffer.
- *
- * This is just like `git_diff_blob_to_buffer()` except it generates a patch
- * object for the difference instead of directly making callbacks. You can
- * use the standard `git_patch` accessor functions to read the patch
- * data, and you must call `git_patch_free()` on the patch when done.
- *
- * @param out The generated patch; NULL on error
- * @param old_blob Blob for old side of diff, or NULL for empty blob
- * @param old_as_path Treat old blob as if it had this filename; can be NULL
- * @param buffer Raw data for new side of diff, or NULL for empty
- * @param buffer_len Length of raw data for new side of diff
- * @param buffer_as_path Treat buffer as if it had this filename; can be NULL
- * @param opts Options for diff, or NULL for default options
- * @return 0 on success or error code < 0
- */
-GIT_EXTERN(int) git_patch_from_blob_and_buffer(
- git_patch **out,
- const git_blob *old_blob,
- const char *old_as_path,
- const char *buffer,
- size_t buffer_len,
- const char *buffer_as_path,
- const git_diff_options *opts);
-
-/**
- * Directly generate a patch from the difference between two buffers.
- *
- * This is just like `git_diff_buffers()` except it generates a patch
- * object for the difference instead of directly making callbacks. You can
- * use the standard `git_patch` accessor functions to read the patch
- * data, and you must call `git_patch_free()` on the patch when done.
- *
- * @param out The generated patch; NULL on error
- * @param old_buffer Raw data for old side of diff, or NULL for empty
- * @param old_len Length of the raw data for old side of the diff
- * @param old_as_path Treat old buffer as if it had this filename; can be NULL
- * @param new_buffer Raw data for new side of diff, or NULL for empty
- * @param new_len Length of raw data for new side of diff
- * @param new_as_path Treat buffer as if it had this filename; can be NULL
- * @param opts Options for diff, or NULL for default options
- * @return 0 on success or error code < 0
- */
-GIT_EXTERN(int) git_patch_from_buffers(
- git_patch **out,
- const void *old_buffer,
- size_t old_len,
- const char *old_as_path,
- const char *new_buffer,
- size_t new_len,
- const char *new_as_path,
- const git_diff_options *opts);
-
-/**
- * Free a git_patch object.
- */
-GIT_EXTERN(void) git_patch_free(git_patch *patch);
-
-/**
- * Get the delta associated with a patch. This delta points to internal
- * data and you do not have to release it when you are done with it.
- */
-GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(const git_patch *patch);
-
-/**
- * Get the number of hunks in a patch
- */
-GIT_EXTERN(size_t) git_patch_num_hunks(const git_patch *patch);
-
-/**
- * Get line counts of each type in a patch.
- *
- * This helps imitate a diff --numstat type of output. For that purpose,
- * you only need the `total_additions` and `total_deletions` values, but we
- * include the `total_context` line count in case you want the total number
- * of lines of diff output that will be generated.
- *
- * All outputs are optional. Pass NULL if you don't need a particular count.
- *
- * @param total_context Count of context lines in output, can be NULL.
- * @param total_additions Count of addition lines in output, can be NULL.
- * @param total_deletions Count of deletion lines in output, can be NULL.
- * @param patch The git_patch object
- * @return 0 on success, <0 on error
- */
-GIT_EXTERN(int) git_patch_line_stats(
- size_t *total_context,
- size_t *total_additions,
- size_t *total_deletions,
- const git_patch *patch);
-
-/**
- * Get the information about a hunk in a patch
- *
- * Given a patch and a hunk index into the patch, this returns detailed
- * information about that hunk. Any of the output pointers can be passed
- * as NULL if you don't care about that particular piece of information.
- *
- * @param out Output pointer to git_diff_hunk of hunk
- * @param lines_in_hunk Output count of total lines in this hunk
- * @param patch Input pointer to patch object
- * @param hunk_idx Input index of hunk to get information about
- * @return 0 on success, GIT_ENOTFOUND if hunk_idx out of range, <0 on error
- */
-GIT_EXTERN(int) git_patch_get_hunk(
- const git_diff_hunk **out,
- size_t *lines_in_hunk,
- git_patch *patch,
- size_t hunk_idx);
-
-/**
- * Get the number of lines in a hunk.
- *
- * @param patch The git_patch object
- * @param hunk_idx Index of the hunk
- * @return Number of lines in hunk or GIT_ENOTFOUND if invalid hunk index
- */
-GIT_EXTERN(int) git_patch_num_lines_in_hunk(
- const git_patch *patch,
- size_t hunk_idx);
-
-/**
- * Get data about a line in a hunk of a patch.
- *
- * Given a patch, a hunk index, and a line index in the hunk, this
- * will return a lot of details about that line. If you pass a hunk
- * index larger than the number of hunks or a line index larger than
- * the number of lines in the hunk, this will return -1.
- *
- * @param out The git_diff_line data for this line
- * @param patch The patch to look in
- * @param hunk_idx The index of the hunk
- * @param line_of_hunk The index of the line in the hunk
- * @return 0 on success, <0 on failure
- */
-GIT_EXTERN(int) git_patch_get_line_in_hunk(
- const git_diff_line **out,
- git_patch *patch,
- size_t hunk_idx,
- size_t line_of_hunk);
-
-/**
- * Look up size of patch diff data in bytes
- *
- * This returns the raw size of the patch data. This only includes the
- * actual data from the lines of the diff, not the file or hunk headers.
- *
- * If you pass `include_context` as true (non-zero), this will be the size
- * of all of the diff output; if you pass it as false (zero), this will
- * only include the actual changed lines (as if `context_lines` was 0).
- *
- * @param patch A git_patch representing changes to one file
- * @param include_context Include context lines in size if non-zero
- * @param include_hunk_headers Include hunk header lines if non-zero
- * @param include_file_headers Include file header lines if non-zero
- * @return The number of bytes of data
- */
-GIT_EXTERN(size_t) git_patch_size(
- git_patch *patch,
- int include_context,
- int include_hunk_headers,
- int include_file_headers);
-
-/**
- * Serialize the patch to text via callback.
- *
- * Returning a non-zero value from the callback will terminate the iteration
- * and return that value to the caller.
- *
- * @param patch A git_patch representing changes to one file
- * @param print_cb Callback function to output lines of the patch. Will be
- * called for file headers, hunk headers, and diff lines.
- * @param payload Reference pointer that will be passed to your callbacks.
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_patch_print(
- git_patch *patch,
- git_diff_line_cb print_cb,
- void *payload);
-
-/**
- * Get the content of a patch as a single diff text.
- *
- * @param out The git_buf to be filled in
- * @param patch A git_patch representing changes to one file
- * @return 0 on success, <0 on failure.
- */
-GIT_EXTERN(int) git_patch_to_buf(
- git_buf *out,
- git_patch *patch);
-
-GIT_END_DECL
-
-/**@}*/
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_pathspec_h__
-#define INCLUDE_git_pathspec_h__
-
-#include "common.h"
-#include "types.h"
-#include "strarray.h"
-#include "diff.h"
-
-GIT_BEGIN_DECL
-
-/**
- * Compiled pathspec
- */
-typedef struct git_pathspec git_pathspec;
-
-/**
- * List of filenames matching a pathspec
- */
-typedef struct git_pathspec_match_list git_pathspec_match_list;
-
-/**
- * Options controlling how pathspec match should be executed
- *
- * - GIT_PATHSPEC_IGNORE_CASE forces match to ignore case; otherwise
- * match will use native case sensitivity of platform filesystem
- * - GIT_PATHSPEC_USE_CASE forces case sensitive match; otherwise
- * match will use native case sensitivity of platform filesystem
- * - GIT_PATHSPEC_NO_GLOB disables glob patterns and just uses simple
- * string comparison for matching
- * - GIT_PATHSPEC_NO_MATCH_ERROR means the match functions return error
- * code GIT_ENOTFOUND if no matches are found; otherwise no matches is
- * still success (return 0) but `git_pathspec_match_list_entrycount`
- * will indicate 0 matches.
- * - GIT_PATHSPEC_FIND_FAILURES means that the `git_pathspec_match_list`
- * should track which patterns matched which files so that at the end of
- * the match we can identify patterns that did not match any files.
- * - GIT_PATHSPEC_FAILURES_ONLY means that the `git_pathspec_match_list`
- * does not need to keep the actual matching filenames. Use this to
- * just test if there were any matches at all or in combination with
- * GIT_PATHSPEC_FIND_FAILURES to validate a pathspec.
- */
-typedef enum {
- GIT_PATHSPEC_DEFAULT = 0,
- GIT_PATHSPEC_IGNORE_CASE = (1u << 0),
- GIT_PATHSPEC_USE_CASE = (1u << 1),
- GIT_PATHSPEC_NO_GLOB = (1u << 2),
- GIT_PATHSPEC_NO_MATCH_ERROR = (1u << 3),
- GIT_PATHSPEC_FIND_FAILURES = (1u << 4),
- GIT_PATHSPEC_FAILURES_ONLY = (1u << 5),
-} git_pathspec_flag_t;
-
-/**
- * Compile a pathspec
- *
- * @param out Output of the compiled pathspec
- * @param pathspec A git_strarray of the paths to match
- * @return 0 on success, <0 on failure
- */
-GIT_EXTERN(int) git_pathspec_new(
- git_pathspec **out, const git_strarray *pathspec);
-
-/**
- * Free a pathspec
- *
- * @param ps The compiled pathspec
- */
-GIT_EXTERN(void) git_pathspec_free(git_pathspec *ps);
-
-/**
- * Try to match a path against a pathspec
- *
- * Unlike most of the other pathspec matching functions, this will not
- * fall back on the native case-sensitivity for your platform. You must
- * explicitly pass flags to control case sensitivity or else this will
- * fall back on being case sensitive.
- *
- * @param ps The compiled pathspec
- * @param flags Combination of git_pathspec_flag_t options to control match
- * @param path The pathname to attempt to match
- * @return 1 is path matches spec, 0 if it does not
- */
-GIT_EXTERN(int) git_pathspec_matches_path(
- const git_pathspec *ps, uint32_t flags, const char *path);
-
-/**
- * Match a pathspec against the working directory of a repository.
- *
- * This matches the pathspec against the current files in the working
- * directory of the repository. It is an error to invoke this on a bare
- * repo. This handles git ignores (i.e. ignored files will not be
- * considered to match the `pathspec` unless the file is tracked in the
- * index).
- *
- * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
- * contains the list of all matched filenames (unless you pass the
- * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
- * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
- * flag). You must call `git_pathspec_match_list_free()` on this object.
- *
- * @param out Output list of matches; pass NULL to just get return value
- * @param repo The repository in which to match; bare repo is an error
- * @param flags Combination of git_pathspec_flag_t options to control match
- * @param ps Pathspec to be matched
- * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
- * the GIT_PATHSPEC_NO_MATCH_ERROR flag was given
- */
-GIT_EXTERN(int) git_pathspec_match_workdir(
- git_pathspec_match_list **out,
- git_repository *repo,
- uint32_t flags,
- git_pathspec *ps);
-
-/**
- * Match a pathspec against entries in an index.
- *
- * This matches the pathspec against the files in the repository index.
- *
- * NOTE: At the moment, the case sensitivity of this match is controlled
- * by the current case-sensitivity of the index object itself and the
- * USE_CASE and IGNORE_CASE flags will have no effect. This behavior will
- * be corrected in a future release.
- *
- * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
- * contains the list of all matched filenames (unless you pass the
- * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
- * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
- * flag). You must call `git_pathspec_match_list_free()` on this object.
- *
- * @param out Output list of matches; pass NULL to just get return value
- * @param index The index to match against
- * @param flags Combination of git_pathspec_flag_t options to control match
- * @param ps Pathspec to be matched
- * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
- * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
- */
-GIT_EXTERN(int) git_pathspec_match_index(
- git_pathspec_match_list **out,
- git_index *index,
- uint32_t flags,
- git_pathspec *ps);
-
-/**
- * Match a pathspec against files in a tree.
- *
- * This matches the pathspec against the files in the given tree.
- *
- * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
- * contains the list of all matched filenames (unless you pass the
- * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
- * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
- * flag). You must call `git_pathspec_match_list_free()` on this object.
- *
- * @param out Output list of matches; pass NULL to just get return value
- * @param tree The root-level tree to match against
- * @param flags Combination of git_pathspec_flag_t options to control match
- * @param ps Pathspec to be matched
- * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
- * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
- */
-GIT_EXTERN(int) git_pathspec_match_tree(
- git_pathspec_match_list **out,
- git_tree *tree,
- uint32_t flags,
- git_pathspec *ps);
-
-/**
- * Match a pathspec against files in a diff list.
- *
- * This matches the pathspec against the files in the given diff list.
- *
- * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
- * contains the list of all matched filenames (unless you pass the
- * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
- * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
- * flag). You must call `git_pathspec_match_list_free()` on this object.
- *
- * @param out Output list of matches; pass NULL to just get return value
- * @param diff A generated diff list
- * @param flags Combination of git_pathspec_flag_t options to control match
- * @param ps Pathspec to be matched
- * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
- * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
- */
-GIT_EXTERN(int) git_pathspec_match_diff(
- git_pathspec_match_list **out,
- git_diff *diff,
- uint32_t flags,
- git_pathspec *ps);
-
-/**
- * Free memory associates with a git_pathspec_match_list
- *
- * @param m The git_pathspec_match_list to be freed
- */
-GIT_EXTERN(void) git_pathspec_match_list_free(git_pathspec_match_list *m);
-
-/**
- * Get the number of items in a match list.
- *
- * @param m The git_pathspec_match_list object
- * @return Number of items in match list
- */
-GIT_EXTERN(size_t) git_pathspec_match_list_entrycount(
- const git_pathspec_match_list *m);
-
-/**
- * Get a matching filename by position.
- *
- * This routine cannot be used if the match list was generated by
- * `git_pathspec_match_diff`. If so, it will always return NULL.
- *
- * @param m The git_pathspec_match_list object
- * @param pos The index into the list
- * @return The filename of the match
- */
-GIT_EXTERN(const char *) git_pathspec_match_list_entry(
- const git_pathspec_match_list *m, size_t pos);
-
-/**
- * Get a matching diff delta by position.
- *
- * This routine can only be used if the match list was generated by
- * `git_pathspec_match_diff`. Otherwise it will always return NULL.
- *
- * @param m The git_pathspec_match_list object
- * @param pos The index into the list
- * @return The filename of the match
- */
-GIT_EXTERN(const git_diff_delta *) git_pathspec_match_list_diff_entry(
- const git_pathspec_match_list *m, size_t pos);
-
-/**
- * Get the number of pathspec items that did not match.
- *
- * This will be zero unless you passed GIT_PATHSPEC_FIND_FAILURES when
- * generating the git_pathspec_match_list.
- *
- * @param m The git_pathspec_match_list object
- * @return Number of items in original pathspec that had no matches
- */
-GIT_EXTERN(size_t) git_pathspec_match_list_failed_entrycount(
- const git_pathspec_match_list *m);
-
-/**
- * Get an original pathspec string that had no matches.
- *
- * This will be return NULL for positions out of range.
- *
- * @param m The git_pathspec_match_list object
- * @param pos The index into the failed items
- * @return The pathspec pattern that didn't match anything
- */
-GIT_EXTERN(const char *) git_pathspec_match_list_failed_entry(
- const git_pathspec_match_list *m, size_t pos);
-
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_proxy_h__
-#define INCLUDE_git_proxy_h__
-
-#include "common.h"
-#include "transport.h"
-
-GIT_BEGIN_DECL
-
-/**
- * The type of proxy to use.
- */
-typedef enum {
- /**
- * Do not attempt to connect through a proxy
- *
- * If built against libcurl, it itself may attempt to connect
- * to a proxy if the environment variables specify it.
- */
- GIT_PROXY_NONE,
- /**
- * Try to auto-detect the proxy from the git configuration.
- */
- GIT_PROXY_AUTO,
- /**
- * Connect via the URL given in the options
- */
- GIT_PROXY_SPECIFIED,
-} git_proxy_t;
-
-/**
- * Options for connecting through a proxy
- *
- * Note that not all types may be supported, depending on the platform
- * and compilation options.
- */
-typedef struct {
- unsigned int version;
-
- /**
- * The type of proxy to use, by URL, auto-detect.
- */
- git_proxy_t type;
-
- /**
- * The URL of the proxy.
- */
- const char *url;
-
- /**
- * This will be called if the remote host requires
- * authentication in order to connect to it.
- *
- * Returning GIT_PASSTHROUGH will make libgit2 behave as
- * though this field isn't set.
- */
- git_cred_acquire_cb credentials;
-
- /**
- * If cert verification fails, this will be called to let the
- * user make the final decision of whether to allow the
- * connection to proceed. Returns 1 to allow the connection, 0
- * to disallow it or a negative value to indicate an error.
- */
- git_transport_certificate_check_cb certificate_check;
-
- /**
- * Payload to be provided to the credentials and certificate
- * check callbacks.
- */
- void *payload;
-} git_proxy_options;
-
-#define GIT_PROXY_OPTIONS_VERSION 1
-#define GIT_PROXY_OPTIONS_INIT {GIT_PROXY_OPTIONS_VERSION}
-
-/**
- * Initialize a proxy options structure
- *
- * @param opts the options struct to initialize
- * @param version the version of the struct, use `GIT_PROXY_OPTIONS_VERSION`
- */
-GIT_EXTERN(int) git_proxy_init_options(git_proxy_options *opts, unsigned int version);
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_rebase_h__
-#define INCLUDE_git_rebase_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "annotated_commit.h"
-
-/**
- * @file git2/rebase.h
- * @brief Git rebase routines
- * @defgroup git_rebase Git merge routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Rebase options
- *
- * Use to tell the rebase machinery how to operate.
- */
-typedef struct {
- unsigned int version;
-
- /**
- * Used by `git_rebase_init`, this will instruct other clients working
- * on this rebase that you want a quiet rebase experience, which they
- * may choose to provide in an application-specific manner. This has no
- * effect upon libgit2 directly, but is provided for interoperability
- * between Git tools.
- */
- int quiet;
-
- /**
- * Used by `git_rebase_init`, this will begin an in-memory rebase,
- * which will allow callers to step through the rebase operations and
- * commit the rebased changes, but will not rewind HEAD or update the
- * repository to be in a rebasing state. This will not interfere with
- * the working directory (if there is one).
- */
- int inmemory;
-
- /**
- * Used by `git_rebase_finish`, this is the name of the notes reference
- * used to rewrite notes for rebased commits when finishing the rebase;
- * if NULL, the contents of the configuration option `notes.rewriteRef`
- * is examined, unless the configuration option `notes.rewrite.rebase`
- * is set to false. If `notes.rewriteRef` is also NULL, notes will
- * not be rewritten.
- */
- const char *rewrite_notes_ref;
-
- /**
- * Options to control how trees are merged during `git_rebase_next`.
- */
- git_merge_options merge_options;
-
- /**
- * Options to control how files are written during `git_rebase_init`,
- * `git_rebase_next` and `git_rebase_abort`. Note that a minimum
- * strategy of `GIT_CHECKOUT_SAFE` is defaulted in `init` and `next`,
- * and a minimum strategy of `GIT_CHECKOUT_FORCE` is defaulted in
- * `abort` to match git semantics.
- */
- git_checkout_options checkout_options;
-} git_rebase_options;
-
-/**
- * Type of rebase operation in-progress after calling `git_rebase_next`.
- */
-typedef enum {
- /**
- * The given commit is to be cherry-picked. The client should commit
- * the changes and continue if there are no conflicts.
- */
- GIT_REBASE_OPERATION_PICK = 0,
-
- /**
- * The given commit is to be cherry-picked, but the client should prompt
- * the user to provide an updated commit message.
- */
- GIT_REBASE_OPERATION_REWORD,
-
- /**
- * The given commit is to be cherry-picked, but the client should stop
- * to allow the user to edit the changes before committing them.
- */
- GIT_REBASE_OPERATION_EDIT,
-
- /**
- * The given commit is to be squashed into the previous commit. The
- * commit message will be merged with the previous message.
- */
- GIT_REBASE_OPERATION_SQUASH,
-
- /**
- * The given commit is to be squashed into the previous commit. The
- * commit message from this commit will be discarded.
- */
- GIT_REBASE_OPERATION_FIXUP,
-
- /**
- * No commit will be cherry-picked. The client should run the given
- * command and (if successful) continue.
- */
- GIT_REBASE_OPERATION_EXEC,
-} git_rebase_operation_t;
-
-#define GIT_REBASE_OPTIONS_VERSION 1
-#define GIT_REBASE_OPTIONS_INIT \
- { GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_MERGE_OPTIONS_INIT, \
- GIT_CHECKOUT_OPTIONS_INIT}
-
-/** Indicates that a rebase operation is not (yet) in progress. */
-#define GIT_REBASE_NO_OPERATION SIZE_MAX
-
-/**
- * A rebase operation
- *
- * Describes a single instruction/operation to be performed during the
- * rebase.
- */
-typedef struct {
- /** The type of rebase operation. */
- git_rebase_operation_t type;
-
- /**
- * The commit ID being cherry-picked. This will be populated for
- * all operations except those of type `GIT_REBASE_OPERATION_EXEC`.
- */
- const git_oid id;
-
- /**
- * The executable the user has requested be run. This will only
- * be populated for operations of type `GIT_REBASE_OPERATION_EXEC`.
- */
- const char *exec;
-} git_rebase_operation;
-
-/**
- * Initializes a `git_rebase_options` with default values. Equivalent to
- * creating an instance with GIT_REBASE_OPTIONS_INIT.
- *
- * @param opts the `git_rebase_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_REBASE_OPTIONS_VERSION` here.
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_rebase_init_options(
- git_rebase_options *opts,
- unsigned int version);
-
-/**
- * Initializes a rebase operation to rebase the changes in `branch`
- * relative to `upstream` onto another branch. To begin the rebase
- * process, call `git_rebase_next`. When you have finished with this
- * object, call `git_rebase_free`.
- *
- * @param out Pointer to store the rebase object
- * @param repo The repository to perform the rebase
- * @param branch The terminal commit to rebase, or NULL to rebase the
- * current branch
- * @param upstream The commit to begin rebasing from, or NULL to rebase all
- * reachable commits
- * @param onto The branch to rebase onto, or NULL to rebase onto the given
- * upstream
- * @param opts Options to specify how rebase is performed, or NULL
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_rebase_init(
- git_rebase **out,
- git_repository *repo,
- const git_annotated_commit *branch,
- const git_annotated_commit *upstream,
- const git_annotated_commit *onto,
- const git_rebase_options *opts);
-
-/**
- * Opens an existing rebase that was previously started by either an
- * invocation of `git_rebase_init` or by another client.
- *
- * @param out Pointer to store the rebase object
- * @param repo The repository that has a rebase in-progress
- * @param opts Options to specify how rebase is performed
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_rebase_open(
- git_rebase **out,
- git_repository *repo,
- const git_rebase_options *opts);
-
-/**
- * Gets the count of rebase operations that are to be applied.
- *
- * @param rebase The in-progress rebase
- * @return The number of rebase operations in total
- */
-GIT_EXTERN(size_t) git_rebase_operation_entrycount(git_rebase *rebase);
-
-/**
- * Gets the index of the rebase operation that is currently being applied.
- * If the first operation has not yet been applied (because you have
- * called `init` but not yet `next`) then this returns
- * `GIT_REBASE_NO_OPERATION`.
- *
- * @param rebase The in-progress rebase
- * @return The index of the rebase operation currently being applied.
- */
-GIT_EXTERN(size_t) git_rebase_operation_current(git_rebase *rebase);
-
-/**
- * Gets the rebase operation specified by the given index.
- *
- * @param rebase The in-progress rebase
- * @param idx The index of the rebase operation to retrieve
- * @return The rebase operation or NULL if `idx` was out of bounds
- */
-GIT_EXTERN(git_rebase_operation *) git_rebase_operation_byindex(
- git_rebase *rebase,
- size_t idx);
-
-/**
- * Performs the next rebase operation and returns the information about it.
- * If the operation is one that applies a patch (which is any operation except
- * GIT_REBASE_OPERATION_EXEC) then the patch will be applied and the index and
- * working directory will be updated with the changes. If there are conflicts,
- * you will need to address those before committing the changes.
- *
- * @param operation Pointer to store the rebase operation that is to be performed next
- * @param rebase The rebase in progress
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_rebase_next(
- git_rebase_operation **operation,
- git_rebase *rebase);
-
-/**
- * Gets the index produced by the last operation, which is the result
- * of `git_rebase_next` and which will be committed by the next
- * invocation of `git_rebase_commit`. This is useful for resolving
- * conflicts in an in-memory rebase before committing them. You must
- * call `git_index_free` when you are finished with this.
- *
- * This is only applicable for in-memory rebases; for rebases within
- * a working directory, the changes were applied to the repository's
- * index.
- */
-GIT_EXTERN(int) git_rebase_inmemory_index(
- git_index **index,
- git_rebase *rebase);
-
-/**
- * Commits the current patch. You must have resolved any conflicts that
- * were introduced during the patch application from the `git_rebase_next`
- * invocation.
- *
- * @param id Pointer in which to store the OID of the newly created commit
- * @param rebase The rebase that is in-progress
- * @param author The author of the updated commit, or NULL to keep the
- * author from the original commit
- * @param committer The committer of the rebase
- * @param message_encoding The encoding for the message in the commit,
- * represented with a standard encoding name. If message is NULL,
- * this should also be NULL, and the encoding from the original
- * commit will be maintained. If message is specified, this may be
- * NULL to indicate that "UTF-8" is to be used.
- * @param message The message for this commit, or NULL to use the message
- * from the original commit.
- * @return Zero on success, GIT_EUNMERGED if there are unmerged changes in
- * the index, GIT_EAPPLIED if the current commit has already
- * been applied to the upstream and there is nothing to commit,
- * -1 on failure.
- */
-GIT_EXTERN(int) git_rebase_commit(
- git_oid *id,
- git_rebase *rebase,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message);
-
-/**
- * Aborts a rebase that is currently in progress, resetting the repository
- * and working directory to their state before rebase began.
- *
- * @param rebase The rebase that is in-progress
- * @return Zero on success; GIT_ENOTFOUND if a rebase is not in progress,
- * -1 on other errors.
- */
-GIT_EXTERN(int) git_rebase_abort(git_rebase *rebase);
-
-/**
- * Finishes a rebase that is currently in progress once all patches have
- * been applied.
- *
- * @param rebase The rebase that is in-progress
- * @param signature The identity that is finishing the rebase (optional)
- * @return Zero on success; -1 on error
- */
-GIT_EXTERN(int) git_rebase_finish(
- git_rebase *rebase,
- const git_signature *signature);
-
-/**
- * Frees the `git_rebase` object.
- *
- * @param rebase The rebase object
- */
-GIT_EXTERN(void) git_rebase_free(git_rebase *rebase);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_refdb_h__
-#define INCLUDE_git_refdb_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "refs.h"
-
-/**
- * @file git2/refdb.h
- * @brief Git custom refs backend functions
- * @defgroup git_refdb Git custom refs backend API
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Create a new reference database with no backends.
- *
- * Before the Ref DB can be used for read/writing, a custom database
- * backend must be manually set using `git_refdb_set_backend()`
- *
- * @param out location to store the database pointer, if opened.
- * Set to NULL if the open failed.
- * @param repo the repository
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_refdb_new(git_refdb **out, git_repository *repo);
-
-/**
- * Create a new reference database and automatically add
- * the default backends:
- *
- * - git_refdb_dir: read and write loose and packed refs
- * from disk, assuming the repository dir as the folder
- *
- * @param out location to store the database pointer, if opened.
- * Set to NULL if the open failed.
- * @param repo the repository
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_refdb_open(git_refdb **out, git_repository *repo);
-
-/**
- * Suggests that the given refdb compress or optimize its references.
- * This mechanism is implementation specific. For on-disk reference
- * databases, for example, this may pack all loose references.
- */
-GIT_EXTERN(int) git_refdb_compress(git_refdb *refdb);
-
-/**
- * Close an open reference database.
- *
- * @param refdb reference database pointer or NULL
- */
-GIT_EXTERN(void) git_refdb_free(git_refdb *refdb);
-
-/** @} */
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_reflog_h__
-#define INCLUDE_git_reflog_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-
-/**
- * @file git2/reflog.h
- * @brief Git reflog management routines
- * @defgroup git_reflog Git reflog management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Read the reflog for the given reference
- *
- * If there is no reflog file for the given
- * reference yet, an empty reflog object will
- * be returned.
- *
- * The reflog must be freed manually by using
- * git_reflog_free().
- *
- * @param out pointer to reflog
- * @param repo the repostiory
- * @param name reference to look up
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reflog_read(git_reflog **out, git_repository *repo, const char *name);
-
-/**
- * Write an existing in-memory reflog object back to disk
- * using an atomic file lock.
- *
- * @param reflog an existing reflog object
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reflog_write(git_reflog *reflog);
-
-/**
- * Add a new entry to the in-memory reflog.
- *
- * `msg` is optional and can be NULL.
- *
- * @param reflog an existing reflog object
- * @param id the OID the reference is now pointing to
- * @param committer the signature of the committer
- * @param msg the reflog message
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg);
-
-/**
- * Rename a reflog
- *
- * The reflog to be renamed is expected to already exist
- *
- * The new name will be checked for validity.
- * See `git_reference_create_symbolic()` for rules about valid names.
- *
- * @param repo the repository
- * @param old_name the old name of the reference
- * @param name the new name of the reference
- * @return 0 on success, GIT_EINVALIDSPEC or an error code
- */
-GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name);
-
-/**
- * Delete the reflog for the given reference
- *
- * @param repo the repository
- * @param name the reflog to delete
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reflog_delete(git_repository *repo, const char *name);
-
-/**
- * Get the number of log entries in a reflog
- *
- * @param reflog the previously loaded reflog
- * @return the number of log entries
- */
-GIT_EXTERN(size_t) git_reflog_entrycount(git_reflog *reflog);
-
-/**
- * Lookup an entry by its index
- *
- * Requesting the reflog entry with an index of 0 (zero) will
- * return the most recently created entry.
- *
- * @param reflog a previously loaded reflog
- * @param idx the position of the entry to lookup. Should be greater than or
- * equal to 0 (zero) and less than `git_reflog_entrycount()`.
- * @return the entry; NULL if not found
- */
-GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(const git_reflog *reflog, size_t idx);
-
-/**
- * Remove an entry from the reflog by its index
- *
- * To ensure there's no gap in the log history, set `rewrite_previous_entry`
- * param value to 1. When deleting entry `n`, member old_oid of entry `n-1`
- * (if any) will be updated with the value of member new_oid of entry `n+1`.
- *
- * @param reflog a previously loaded reflog.
- *
- * @param idx the position of the entry to remove. Should be greater than or
- * equal to 0 (zero) and less than `git_reflog_entrycount()`.
- *
- * @param rewrite_previous_entry 1 to rewrite the history; 0 otherwise.
- *
- * @return 0 on success, GIT_ENOTFOUND if the entry doesn't exist
- * or an error code.
- */
-GIT_EXTERN(int) git_reflog_drop(
- git_reflog *reflog,
- size_t idx,
- int rewrite_previous_entry);
-
-/**
- * Get the old oid
- *
- * @param entry a reflog entry
- * @return the old oid
- */
-GIT_EXTERN(const git_oid *) git_reflog_entry_id_old(const git_reflog_entry *entry);
-
-/**
- * Get the new oid
- *
- * @param entry a reflog entry
- * @return the new oid at this time
- */
-GIT_EXTERN(const git_oid *) git_reflog_entry_id_new(const git_reflog_entry *entry);
-
-/**
- * Get the committer of this entry
- *
- * @param entry a reflog entry
- * @return the committer
- */
-GIT_EXTERN(const git_signature *) git_reflog_entry_committer(const git_reflog_entry *entry);
-
-/**
- * Get the log message
- *
- * @param entry a reflog entry
- * @return the log msg
- */
-GIT_EXTERN(const char *) git_reflog_entry_message(const git_reflog_entry *entry);
-
-/**
- * Free the reflog
- *
- * @param reflog reflog to free
- */
-GIT_EXTERN(void) git_reflog_free(git_reflog *reflog);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_refs_h__
-#define INCLUDE_git_refs_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "strarray.h"
-
-/**
- * @file git2/refs.h
- * @brief Git reference management routines
- * @defgroup git_reference Git reference management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Lookup a reference by name in a repository.
- *
- * The returned reference must be freed by the user.
- *
- * The name will be checked for validity.
- * See `git_reference_symbolic_create()` for rules about valid names.
- *
- * @param out pointer to the looked-up reference
- * @param repo the repository to look up the reference
- * @param name the long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
- * @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code.
- */
-GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, const char *name);
-
-/**
- * Lookup a reference by name and resolve immediately to OID.
- *
- * This function provides a quick way to resolve a reference name straight
- * through to the object id that it refers to. This avoids having to
- * allocate or free any `git_reference` objects for simple situations.
- *
- * The name will be checked for validity.
- * See `git_reference_symbolic_create()` for rules about valid names.
- *
- * @param out Pointer to oid to be filled in
- * @param repo The repository in which to look up the reference
- * @param name The long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
- * @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code.
- */
-GIT_EXTERN(int) git_reference_name_to_id(
- git_oid *out, git_repository *repo, const char *name);
-
-/**
- * Lookup a reference by DWIMing its short name
- *
- * Apply the git precendence rules to the given shorthand to determine
- * which reference the user is referring to.
- *
- * @param out pointer in which to store the reference
- * @param repo the repository in which to look
- * @param shorthand the short name for the reference
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, const char *shorthand);
-
-/**
- * Conditionally create a new symbolic reference.
- *
- * A symbolic reference is a reference name that refers to another
- * reference name. If the other name moves, the symbolic name will move,
- * too. As a simple example, the "HEAD" reference might refer to
- * "refs/heads/master" while on the "master" branch of a repository.
- *
- * The symbolic reference will be created in the repository and written to
- * the disk. The generated reference object must be freed by the user.
- *
- * Valid reference names must follow one of two patterns:
- *
- * 1. Top-level names must contain only capital letters and underscores,
- * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
- * 2. Names prefixed with "refs/" can be almost anything. You must avoid
- * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
- * sequences ".." and "@{" which have special meaning to revparse.
- *
- * This function will return an error if a reference already exists with the
- * given name unless `force` is true, in which case it will be overwritten.
- *
- * The message for the reflog will be ignored if the reference does
- * not belong in the standard set (HEAD, branches and remote-tracking
- * branches) and it does not have a reflog.
- *
- * It will return GIT_EMODIFIED if the reference's value at the time
- * of updating does not match the one passed through `current_value`
- * (i.e. if the ref has changed since the user read it).
- *
- * @param out Pointer to the newly created reference
- * @param repo Repository where that reference will live
- * @param name The name of the reference
- * @param target The target of the reference
- * @param force Overwrite existing references
- * @param current_value The expected value of the reference when updating
- * @param log_message The one line long message to be appended to the reflog
- * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC, GIT_EMODIFIED or an error code
- */
-GIT_EXTERN(int) git_reference_symbolic_create_matching(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const char *current_value, const char *log_message);
-
-/**
- * Create a new symbolic reference.
- *
- * A symbolic reference is a reference name that refers to another
- * reference name. If the other name moves, the symbolic name will move,
- * too. As a simple example, the "HEAD" reference might refer to
- * "refs/heads/master" while on the "master" branch of a repository.
- *
- * The symbolic reference will be created in the repository and written to
- * the disk. The generated reference object must be freed by the user.
- *
- * Valid reference names must follow one of two patterns:
- *
- * 1. Top-level names must contain only capital letters and underscores,
- * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
- * 2. Names prefixed with "refs/" can be almost anything. You must avoid
- * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
- * sequences ".." and "@{" which have special meaning to revparse.
- *
- * This function will return an error if a reference already exists with the
- * given name unless `force` is true, in which case it will be overwritten.
- *
- * The message for the reflog will be ignored if the reference does
- * not belong in the standard set (HEAD, branches and remote-tracking
- * branches) and it does not have a reflog.
- *
- * @param out Pointer to the newly created reference
- * @param repo Repository where that reference will live
- * @param name The name of the reference
- * @param target The target of the reference
- * @param force Overwrite existing references
- * @param log_message The one line long message to be appended to the reflog
- * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
- */
-GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const char *log_message);
-
-/**
- * Create a new direct reference.
- *
- * A direct reference (also called an object id reference) refers directly
- * to a specific object id (a.k.a. OID or SHA) in the repository. The id
- * permanently refers to the object (although the reference itself can be
- * moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0"
- * refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977.
- *
- * The direct reference will be created in the repository and written to
- * the disk. The generated reference object must be freed by the user.
- *
- * Valid reference names must follow one of two patterns:
- *
- * 1. Top-level names must contain only capital letters and underscores,
- * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
- * 2. Names prefixed with "refs/" can be almost anything. You must avoid
- * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
- * sequences ".." and "@{" which have special meaning to revparse.
- *
- * This function will return an error if a reference already exists with the
- * given name unless `force` is true, in which case it will be overwritten.
- *
- * The message for the reflog will be ignored if the reference does
- * not belong in the standard set (HEAD, branches and remote-tracking
- * branches) and and it does not have a reflog.
- *
- * @param out Pointer to the newly created reference
- * @param repo Repository where that reference will live
- * @param name The name of the reference
- * @param id The object id pointed to by the reference.
- * @param force Overwrite existing references
- * @param log_message The one line long message to be appended to the reflog
- * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
- */
-GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const char *log_message);
-
-/**
- * Conditionally create new direct reference
- *
- * A direct reference (also called an object id reference) refers directly
- * to a specific object id (a.k.a. OID or SHA) in the repository. The id
- * permanently refers to the object (although the reference itself can be
- * moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0"
- * refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977.
- *
- * The direct reference will be created in the repository and written to
- * the disk. The generated reference object must be freed by the user.
- *
- * Valid reference names must follow one of two patterns:
- *
- * 1. Top-level names must contain only capital letters and underscores,
- * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
- * 2. Names prefixed with "refs/" can be almost anything. You must avoid
- * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
- * sequences ".." and "@{" which have special meaning to revparse.
- *
- * This function will return an error if a reference already exists with the
- * given name unless `force` is true, in which case it will be overwritten.
- *
- * The message for the reflog will be ignored if the reference does
- * not belong in the standard set (HEAD, branches and remote-tracking
- * branches) and and it does not have a reflog.
- *
- * It will return GIT_EMODIFIED if the reference's value at the time
- * of updating does not match the one passed through `current_id`
- * (i.e. if the ref has changed since the user read it).
- *
- * @param out Pointer to the newly created reference
- * @param repo Repository where that reference will live
- * @param name The name of the reference
- * @param id The object id pointed to by the reference.
- * @param force Overwrite existing references
- * @param current_id The expected value of the reference at the time of update
- * @param log_message The one line long message to be appended to the reflog
- * @return 0 on success, GIT_EMODIFIED if the value of the reference
- * has changed, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
- */
-GIT_EXTERN(int) git_reference_create_matching(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const git_oid *current_id, const char *log_message);
-
-/**
- * Get the OID pointed to by a direct reference.
- *
- * Only available if the reference is direct (i.e. an object id reference,
- * not a symbolic one).
- *
- * To find the OID of a symbolic ref, call `git_reference_resolve()` and
- * then this function (or maybe use `git_reference_name_to_id()` to
- * directly resolve a reference name all the way through to an OID).
- *
- * @param ref The reference
- * @return a pointer to the oid if available, NULL otherwise
- */
-GIT_EXTERN(const git_oid *) git_reference_target(const git_reference *ref);
-
-/**
- * Return the peeled OID target of this reference.
- *
- * This peeled OID only applies to direct references that point to
- * a hard Tag object: it is the result of peeling such Tag.
- *
- * @param ref The reference
- * @return a pointer to the oid if available, NULL otherwise
- */
-GIT_EXTERN(const git_oid *) git_reference_target_peel(const git_reference *ref);
-
-/**
- * Get full name to the reference pointed to by a symbolic reference.
- *
- * Only available if the reference is symbolic.
- *
- * @param ref The reference
- * @return a pointer to the name if available, NULL otherwise
- */
-GIT_EXTERN(const char *) git_reference_symbolic_target(const git_reference *ref);
-
-/**
- * Get the type of a reference.
- *
- * Either direct (GIT_REF_OID) or symbolic (GIT_REF_SYMBOLIC)
- *
- * @param ref The reference
- * @return the type
- */
-GIT_EXTERN(git_ref_t) git_reference_type(const git_reference *ref);
-
-/**
- * Get the full name of a reference.
- *
- * See `git_reference_symbolic_create()` for rules about valid names.
- *
- * @param ref The reference
- * @return the full name for the ref
- */
-GIT_EXTERN(const char *) git_reference_name(const git_reference *ref);
-
-/**
- * Resolve a symbolic reference to a direct reference.
- *
- * This method iteratively peels a symbolic reference until it resolves to
- * a direct reference to an OID.
- *
- * The peeled reference is returned in the `resolved_ref` argument, and
- * must be freed manually once it's no longer needed.
- *
- * If a direct reference is passed as an argument, a copy of that
- * reference is returned. This copy must be manually freed too.
- *
- * @param out Pointer to the peeled reference
- * @param ref The reference
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reference_resolve(git_reference **out, const git_reference *ref);
-
-/**
- * Get the repository where a reference resides.
- *
- * @param ref The reference
- * @return a pointer to the repo
- */
-GIT_EXTERN(git_repository *) git_reference_owner(const git_reference *ref);
-
-/**
- * Create a new reference with the same name as the given reference but a
- * different symbolic target. The reference must be a symbolic reference,
- * otherwise this will fail.
- *
- * The new reference will be written to disk, overwriting the given reference.
- *
- * The target name will be checked for validity.
- * See `git_reference_symbolic_create()` for rules about valid names.
- *
- * The message for the reflog will be ignored if the reference does
- * not belong in the standard set (HEAD, branches and remote-tracking
- * branches) and and it does not have a reflog.
- *
- * @param out Pointer to the newly created reference
- * @param ref The reference
- * @param target The new target for the reference
- * @param log_message The one line long message to be appended to the reflog
- * @return 0 on success, GIT_EINVALIDSPEC or an error code
- */
-GIT_EXTERN(int) git_reference_symbolic_set_target(
- git_reference **out,
- git_reference *ref,
- const char *target,
- const char *log_message);
-
-/**
- * Conditionally create a new reference with the same name as the given reference but a
- * different OID target. The reference must be a direct reference, otherwise
- * this will fail.
- *
- * The new reference will be written to disk, overwriting the given reference.
- *
- * @param out Pointer to the newly created reference
- * @param ref The reference
- * @param id The new target OID for the reference
- * @param log_message The one line long message to be appended to the reflog
- * @return 0 on success, GIT_EMODIFIED if the value of the reference
- * has changed since it was read, or an error code
- */
-GIT_EXTERN(int) git_reference_set_target(
- git_reference **out,
- git_reference *ref,
- const git_oid *id,
- const char *log_message);
-
-/**
- * Rename an existing reference.
- *
- * This method works for both direct and symbolic references.
- *
- * The new name will be checked for validity.
- * See `git_reference_symbolic_create()` for rules about valid names.
- *
- * If the `force` flag is not enabled, and there's already
- * a reference with the given name, the renaming will fail.
- *
- * IMPORTANT:
- * The user needs to write a proper reflog entry if the
- * reflog is enabled for the repository. We only rename
- * the reflog if it exists.
- *
- * @param ref The reference to rename
- * @param new_name The new name for the reference
- * @param force Overwrite an existing reference
- * @param log_message The one line long message to be appended to the reflog
- * @return 0 on success, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
- *
- */
-GIT_EXTERN(int) git_reference_rename(
- git_reference **new_ref,
- git_reference *ref,
- const char *new_name,
- int force,
- const char *log_message);
-
-/**
- * Delete an existing reference.
- *
- * This method works for both direct and symbolic references. The reference
- * will be immediately removed on disk but the memory will not be freed.
- * Callers must call `git_reference_free`.
- *
- * This function will return an error if the reference has changed
- * from the time it was looked up.
- *
- * @param ref The reference to remove
- * @return 0, GIT_EMODIFIED or an error code
- */
-GIT_EXTERN(int) git_reference_delete(git_reference *ref);
-
-/**
- * Delete an existing reference by name
- *
- * This method removes the named reference from the repository without
- * looking at its old value.
- *
- * @param name The reference to remove
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reference_remove(git_repository *repo, const char *name);
-
-/**
- * Fill a list with all the references that can be found in a repository.
- *
- * The string array will be filled with the names of all references; these
- * values are owned by the user and should be free'd manually when no
- * longer needed, using `git_strarray_free()`.
- *
- * @param array Pointer to a git_strarray structure where
- * the reference names will be stored
- * @param repo Repository where to find the refs
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo);
-
-typedef int (*git_reference_foreach_cb)(git_reference *reference, void *payload);
-typedef int (*git_reference_foreach_name_cb)(const char *name, void *payload);
-
-/**
- * Perform a callback on each reference in the repository.
- *
- * The `callback` function will be called for each reference in the
- * repository, receiving the reference object and the `payload` value
- * passed to this method. Returning a non-zero value from the callback
- * will terminate the iteration.
- *
- * @param repo Repository where to find the refs
- * @param callback Function which will be called for every listed ref
- * @param payload Additional data to pass to the callback
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_reference_foreach(
- git_repository *repo,
- git_reference_foreach_cb callback,
- void *payload);
-
-/**
- * Perform a callback on the fully-qualified name of each reference.
- *
- * The `callback` function will be called for each reference in the
- * repository, receiving the name of the reference and the `payload` value
- * passed to this method. Returning a non-zero value from the callback
- * will terminate the iteration.
- *
- * @param repo Repository where to find the refs
- * @param callback Function which will be called for every listed ref name
- * @param payload Additional data to pass to the callback
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_reference_foreach_name(
- git_repository *repo,
- git_reference_foreach_name_cb callback,
- void *payload);
-
-/**
- * Create a copy of an existing reference.
- *
- * Call `git_reference_free` to free the data.
- *
- * @param dest pointer where to store the copy
- * @param source object to copy
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reference_dup(git_reference **dest, git_reference *source);
-
-/**
- * Free the given reference.
- *
- * @param ref git_reference
- */
-GIT_EXTERN(void) git_reference_free(git_reference *ref);
-
-/**
- * Compare two references.
- *
- * @param ref1 The first git_reference
- * @param ref2 The second git_reference
- * @return 0 if the same, else a stable but meaningless ordering.
- */
-GIT_EXTERN(int) git_reference_cmp(
- const git_reference *ref1,
- const git_reference *ref2);
-
-/**
- * Create an iterator for the repo's references
- *
- * @param out pointer in which to store the iterator
- * @param repo the repository
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reference_iterator_new(
- git_reference_iterator **out,
- git_repository *repo);
-
-/**
- * Create an iterator for the repo's references that match the
- * specified glob
- *
- * @param out pointer in which to store the iterator
- * @param repo the repository
- * @param glob the glob to match against the reference names
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_reference_iterator_glob_new(
- git_reference_iterator **out,
- git_repository *repo,
- const char *glob);
-
-/**
- * Get the next reference
- *
- * @param out pointer in which to store the reference
- * @param iter the iterator
- * @return 0, GIT_ITEROVER if there are no more; or an error code
- */
-GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter);
-
-/**
- * Get the next reference's name
- *
- * This function is provided for convenience in case only the names
- * are interesting as it avoids the allocation of the `git_reference`
- * object which `git_reference_next()` needs.
- *
- * @param out pointer in which to store the string
- * @param iter the iterator
- * @return 0, GIT_ITEROVER if there are no more; or an error code
- */
-GIT_EXTERN(int) git_reference_next_name(const char **out, git_reference_iterator *iter);
-
-/**
- * Free the iterator and its associated resources
- *
- * @param iter the iterator to free
- */
-GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter);
-
-/**
- * Perform a callback on each reference in the repository whose name
- * matches the given pattern.
- *
- * This function acts like `git_reference_foreach()` with an additional
- * pattern match being applied to the reference name before issuing the
- * callback function. See that function for more information.
- *
- * The pattern is matched using fnmatch or "glob" style where a '*' matches
- * any sequence of letters, a '?' matches any letter, and square brackets
- * can be used to define character ranges (such as "[0-9]" for digits).
- *
- * @param repo Repository where to find the refs
- * @param glob Pattern to match (fnmatch-style) against reference name.
- * @param callback Function which will be called for every listed ref
- * @param payload Additional data to pass to the callback
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
- */
-GIT_EXTERN(int) git_reference_foreach_glob(
- git_repository *repo,
- const char *glob,
- git_reference_foreach_name_cb callback,
- void *payload);
-
-/**
- * Check if a reflog exists for the specified reference.
- *
- * @param repo the repository
- * @param refname the reference's name
- * @return 0 when no reflog can be found, 1 when it exists;
- * otherwise an error code.
- */
-GIT_EXTERN(int) git_reference_has_log(git_repository *repo, const char *refname);
-
-/**
- * Ensure there is a reflog for a particular reference.
- *
- * Make sure that successive updates to the reference will append to
- * its log.
- *
- * @param repo the repository
- * @param refname the reference's name
- * @return 0 or an error code.
- */
-GIT_EXTERN(int) git_reference_ensure_log(git_repository *repo, const char *refname);
-
-/**
- * Check if a reference is a local branch.
- *
- * @param ref A git reference
- *
- * @return 1 when the reference lives in the refs/heads
- * namespace; 0 otherwise.
- */
-GIT_EXTERN(int) git_reference_is_branch(const git_reference *ref);
-
-/**
- * Check if a reference is a remote tracking branch
- *
- * @param ref A git reference
- *
- * @return 1 when the reference lives in the refs/remotes
- * namespace; 0 otherwise.
- */
-GIT_EXTERN(int) git_reference_is_remote(const git_reference *ref);
-
-/**
- * Check if a reference is a tag
- *
- * @param ref A git reference
- *
- * @return 1 when the reference lives in the refs/tags
- * namespace; 0 otherwise.
- */
-GIT_EXTERN(int) git_reference_is_tag(const git_reference *ref);
-
-/**
- * Check if a reference is a note
- *
- * @param ref A git reference
- *
- * @return 1 when the reference lives in the refs/notes
- * namespace; 0 otherwise.
- */
-GIT_EXTERN(int) git_reference_is_note(const git_reference *ref);
-
-/**
- * Normalization options for reference lookup
- */
-typedef enum {
- /**
- * No particular normalization.
- */
- GIT_REF_FORMAT_NORMAL = 0u,
-
- /**
- * Control whether one-level refnames are accepted
- * (i.e., refnames that do not contain multiple /-separated
- * components). Those are expected to be written only using
- * uppercase letters and underscore (FETCH_HEAD, ...)
- */
- GIT_REF_FORMAT_ALLOW_ONELEVEL = (1u << 0),
-
- /**
- * Interpret the provided name as a reference pattern for a
- * refspec (as used with remote repositories). If this option
- * is enabled, the name is allowed to contain a single * (<star>)
- * in place of a one full pathname component
- * (e.g., foo/<star>/bar but not foo/bar<star>).
- */
- GIT_REF_FORMAT_REFSPEC_PATTERN = (1u << 1),
-
- /**
- * Interpret the name as part of a refspec in shorthand form
- * so the `ONELEVEL` naming rules aren't enforced and 'master'
- * becomes a valid name.
- */
- GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1u << 2),
-} git_reference_normalize_t;
-
-/**
- * Normalize reference name and check validity.
- *
- * This will normalize the reference name by removing any leading slash
- * '/' characters and collapsing runs of adjacent slashes between name
- * components into a single slash.
- *
- * Once normalized, if the reference name is valid, it will be returned in
- * the user allocated buffer.
- *
- * See `git_reference_symbolic_create()` for rules about valid names.
- *
- * @param buffer_out User allocated buffer to store normalized name
- * @param buffer_size Size of buffer_out
- * @param name Reference name to be checked.
- * @param flags Flags to constrain name validation rules - see the
- * GIT_REF_FORMAT constants above.
- * @return 0 on success, GIT_EBUFS if buffer is too small, GIT_EINVALIDSPEC
- * or an error code.
- */
-GIT_EXTERN(int) git_reference_normalize_name(
- char *buffer_out,
- size_t buffer_size,
- const char *name,
- unsigned int flags);
-
-/**
- * Recursively peel reference until object of the specified type is found.
- *
- * The retrieved `peeled` object is owned by the repository
- * and should be closed with the `git_object_free` method.
- *
- * If you pass `GIT_OBJ_ANY` as the target type, then the object
- * will be peeled until a non-tag object is met.
- *
- * @param out Pointer to the peeled git_object
- * @param ref The reference to be processed
- * @param type The type of the requested object (GIT_OBJ_COMMIT,
- * GIT_OBJ_TAG, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_ANY).
- * @return 0 on success, GIT_EAMBIGUOUS, GIT_ENOTFOUND or an error code
- */
-GIT_EXTERN(int) git_reference_peel(
- git_object **out,
- git_reference *ref,
- git_otype type);
-
-/**
- * Ensure the reference name is well-formed.
- *
- * Valid reference names must follow one of two patterns:
- *
- * 1. Top-level names must contain only capital letters and underscores,
- * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
- * 2. Names prefixed with "refs/" can be almost anything. You must avoid
- * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
- * sequences ".." and "@{" which have special meaning to revparse.
- *
- * @param refname name to be checked.
- * @return 1 if the reference name is acceptable; 0 if it isn't
- */
-GIT_EXTERN(int) git_reference_is_valid_name(const char *refname);
-
-/**
- * Get the reference's short name
- *
- * This will transform the reference name into a name "human-readable"
- * version. If no shortname is appropriate, it will return the full
- * name.
- *
- * The memory is owned by the reference and must not be freed.
- *
- * @param ref a reference
- * @return the human-readable version of the name
- */
-GIT_EXTERN(const char *) git_reference_shorthand(const git_reference *ref);
-
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_refspec_h__
-#define INCLUDE_git_refspec_h__
-
-#include "common.h"
-#include "types.h"
-#include "net.h"
-#include "buffer.h"
-
-/**
- * @file git2/refspec.h
- * @brief Git refspec attributes
- * @defgroup git_refspec Git refspec attributes
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Get the source specifier
- *
- * @param refspec the refspec
- * @return the refspec's source specifier
- */
-GIT_EXTERN(const char *) git_refspec_src(const git_refspec *refspec);
-
-/**
- * Get the destination specifier
- *
- * @param refspec the refspec
- * @return the refspec's destination specifier
- */
-GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
-
-/**
- * Get the refspec's string
- *
- * @param refspec the refspec
- * @returns the refspec's original string
- */
-GIT_EXTERN(const char *) git_refspec_string(const git_refspec *refspec);
-
-/**
- * Get the force update setting
- *
- * @param refspec the refspec
- * @return 1 if force update has been set, 0 otherwise
- */
-GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec);
-
-/**
- * Get the refspec's direction.
- *
- * @param spec refspec
- * @return GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
- */
-GIT_EXTERN(git_direction) git_refspec_direction(const git_refspec *spec);
-
-/**
- * Check if a refspec's source descriptor matches a reference
- *
- * @param refspec the refspec
- * @param refname the name of the reference to check
- * @return 1 if the refspec matches, 0 otherwise
- */
-GIT_EXTERN(int) git_refspec_src_matches(const git_refspec *refspec, const char *refname);
-
-/**
- * Check if a refspec's destination descriptor matches a reference
- *
- * @param refspec the refspec
- * @param refname the name of the reference to check
- * @return 1 if the refspec matches, 0 otherwise
- */
-GIT_EXTERN(int) git_refspec_dst_matches(const git_refspec *refspec, const char *refname);
-
-/**
- * Transform a reference to its target following the refspec's rules
- *
- * @param out where to store the target name
- * @param spec the refspec
- * @param name the name of the reference to transform
- * @return 0, GIT_EBUFS or another error
- */
-GIT_EXTERN(int) git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name);
-
-/**
- * Transform a target reference to its source reference following the refspec's rules
- *
- * @param out where to store the source reference name
- * @param spec the refspec
- * @param name the name of the reference to transform
- * @return 0, GIT_EBUFS or another error
- */
-GIT_EXTERN(int) git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name);
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_remote_h__
-#define INCLUDE_git_remote_h__
-
-#include "common.h"
-#include "repository.h"
-#include "refspec.h"
-#include "net.h"
-#include "indexer.h"
-#include "strarray.h"
-#include "transport.h"
-#include "pack.h"
-#include "proxy.h"
-
-/**
- * @file git2/remote.h
- * @brief Git remote management functions
- * @defgroup git_remote remote management functions
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Add a remote with the default fetch refspec to the repository's configuration.
- *
- * @param out the resulting remote
- * @param repo the repository in which to create the remote
- * @param name the remote's name
- * @param url the remote's url
- * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
- */
-GIT_EXTERN(int) git_remote_create(
- git_remote **out,
- git_repository *repo,
- const char *name,
- const char *url);
-
-/**
- * Add a remote with the provided fetch refspec (or default if NULL) to the repository's
- * configuration.
- *
- * @param out the resulting remote
- * @param repo the repository in which to create the remote
- * @param name the remote's name
- * @param url the remote's url
- * @param fetch the remote fetch value
- * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
- */
-GIT_EXTERN(int) git_remote_create_with_fetchspec(
- git_remote **out,
- git_repository *repo,
- const char *name,
- const char *url,
- const char *fetch);
-
-/**
- * Create an anonymous remote
- *
- * Create a remote with the given url in-memory. You can use this when
- * you have a URL instead of a remote's name.
- *
- * @param out pointer to the new remote objects
- * @param repo the associated repository
- * @param url the remote repository's URL
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_remote_create_anonymous(
- git_remote **out,
- git_repository *repo,
- const char *url);
-
-/**
- * Get the information for a particular remote
- *
- * The name will be checked for validity.
- * See `git_tag_create()` for rules about valid names.
- *
- * @param out pointer to the new remote object
- * @param repo the associated repository
- * @param name the remote's name
- * @return 0, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code
- */
-GIT_EXTERN(int) git_remote_lookup(git_remote **out, git_repository *repo, const char *name);
-
-/**
- * Create a copy of an existing remote. All internal strings are also
- * duplicated. Callbacks are not duplicated.
- *
- * Call `git_remote_free` to free the data.
- *
- * @param dest pointer where to store the copy
- * @param source object to copy
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_remote_dup(git_remote **dest, git_remote *source);
-
-/**
- * Get the remote's repository
- *
- * @param remote the remote
- * @return a pointer to the repository
- */
-GIT_EXTERN(git_repository *) git_remote_owner(const git_remote *remote);
-
-/**
- * Get the remote's name
- *
- * @param remote the remote
- * @return a pointer to the name or NULL for in-memory remotes
- */
-GIT_EXTERN(const char *) git_remote_name(const git_remote *remote);
-
-/**
- * Get the remote's url
- *
- * If url.*.insteadOf has been configured for this URL, it will
- * return the modified URL.
- *
- * @param remote the remote
- * @return a pointer to the url
- */
-GIT_EXTERN(const char *) git_remote_url(const git_remote *remote);
-
-/**
- * Get the remote's url for pushing
- *
- * If url.*.pushInsteadOf has been configured for this URL, it
- * will return the modified URL.
- *
- * @param remote the remote
- * @return a pointer to the url or NULL if no special url for pushing is set
- */
-GIT_EXTERN(const char *) git_remote_pushurl(const git_remote *remote);
-
-/**
- * Set the remote's url in the configuration
- *
- * Remote objects already in memory will not be affected. This assumes
- * the common case of a single-url remote and will otherwise return an error.
- *
- * @param repo the repository in which to perform the change
- * @param remote the remote's name
- * @param url the url to set
- * @return 0 or an error value
- */
-GIT_EXTERN(int) git_remote_set_url(git_repository *repo, const char *remote, const char* url);
-
-/**
- * Set the remote's url for pushing in the configuration.
- *
- * Remote objects already in memory will not be affected. This assumes
- * the common case of a single-url remote and will otherwise return an error.
- *
- *
- * @param repo the repository in which to perform the change
- * @param remote the remote's name
- * @param url the url to set
- */
-GIT_EXTERN(int) git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url);
-
-/**
- * Add a fetch refspec to the remote's configuration
- *
- * Add the given refspec to the fetch list in the configuration. No
- * loaded remote instances will be affected.
- *
- * @param repo the repository in which to change the configuration
- * @param remote the name of the remote to change
- * @param refspec the new fetch refspec
- * @return 0, GIT_EINVALIDSPEC if refspec is invalid or an error value
- */
-GIT_EXTERN(int) git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec);
-
-/**
- * Get the remote's list of fetch refspecs
- *
- * The memory is owned by the user and should be freed with
- * `git_strarray_free`.
- *
- * @param array pointer to the array in which to store the strings
- * @param remote the remote to query
- */
-GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote);
-
-/**
- * Add a push refspec to the remote's configuration
- *
- * Add the given refspec to the push list in the configuration. No
- * loaded remote instances will be affected.
- *
- * @param repo the repository in which to change the configuration
- * @param remote the name of the remote to change
- * @param refspec the new push refspec
- * @return 0, GIT_EINVALIDSPEC if refspec is invalid or an error value
- */
-GIT_EXTERN(int) git_remote_add_push(git_repository *repo, const char *remote, const char *refspec);
-
-/**
- * Get the remote's list of push refspecs
- *
- * The memory is owned by the user and should be freed with
- * `git_strarray_free`.
- *
- * @param array pointer to the array in which to store the strings
- * @param remote the remote to query
- */
-GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote);
-
-/**
- * Get the number of refspecs for a remote
- *
- * @param remote the remote
- * @return the amount of refspecs configured in this remote
- */
-GIT_EXTERN(size_t) git_remote_refspec_count(const git_remote *remote);
-
-/**
- * Get a refspec from the remote
- *
- * @param remote the remote to query
- * @param n the refspec to get
- * @return the nth refspec
- */
-GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, size_t n);
-
-/**
- * Open a connection to a remote
- *
- * The transport is selected based on the URL. The direction argument
- * is due to a limitation of the git protocol (over TCP or SSH) which
- * starts up a specific binary which can only do the one or the other.
- *
- * @param remote the remote to connect to
- * @param direction GIT_DIRECTION_FETCH if you want to fetch or
- * GIT_DIRECTION_PUSH if you want to push
- * @param callbacks the callbacks to use for this connection
- * @param proxy_opts proxy settings
- * @param custom_headers extra HTTP headers to use in this connection
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy_opts, const git_strarray *custom_headers);
-
-/**
- * Get the remote repository's reference advertisement list
- *
- * Get the list of references with which the server responds to a new
- * connection.
- *
- * The remote (or more exactly its transport) must have connected to
- * the remote repository. This list is available as soon as the
- * connection to the remote is initiated and it remains available
- * after disconnecting.
- *
- * The memory belongs to the remote. The pointer will be valid as long
- * as a new connection is not initiated, but it is recommended that
- * you make a copy in order to make use of the data.
- *
- * @param out pointer to the array
- * @param size the number of remote heads
- * @param remote the remote
- * @return 0 on success, or an error code
- */
-GIT_EXTERN(int) git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote);
-
-/**
- * Check whether the remote is connected
- *
- * Check whether the remote's underlying transport is connected to the
- * remote host.
- *
- * @param remote the remote
- * @return 1 if it's connected, 0 otherwise.
- */
-GIT_EXTERN(int) git_remote_connected(const git_remote *remote);
-
-/**
- * Cancel the operation
- *
- * At certain points in its operation, the network code checks whether
- * the operation has been cancelled and if so stops the operation.
- *
- * @param remote the remote
- */
-GIT_EXTERN(void) git_remote_stop(git_remote *remote);
-
-/**
- * Disconnect from the remote
- *
- * Close the connection to the remote.
- *
- * @param remote the remote to disconnect from
- */
-GIT_EXTERN(void) git_remote_disconnect(git_remote *remote);
-
-/**
- * Free the memory associated with a remote
- *
- * This also disconnects from the remote, if the connection
- * has not been closed yet (using git_remote_disconnect).
- *
- * @param remote the remote to free
- */
-GIT_EXTERN(void) git_remote_free(git_remote *remote);
-
-/**
- * Get a list of the configured remotes for a repo
- *
- * The string array must be freed by the user.
- *
- * @param out a string array which receives the names of the remotes
- * @param repo the repository to query
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_remote_list(git_strarray *out, git_repository *repo);
-
-/**
- * Argument to the completion callback which tells it which operation
- * finished.
- */
-typedef enum git_remote_completion_type {
- GIT_REMOTE_COMPLETION_DOWNLOAD,
- GIT_REMOTE_COMPLETION_INDEXING,
- GIT_REMOTE_COMPLETION_ERROR,
-} git_remote_completion_type;
-
-/** Push network progress notification function */
-typedef int (*git_push_transfer_progress)(
- unsigned int current,
- unsigned int total,
- size_t bytes,
- void* payload);
-/**
- * Represents an update which will be performed on the remote during push
- */
-typedef struct {
- /**
- * The source name of the reference
- */
- char *src_refname;
- /**
- * The name of the reference to update on the server
- */
- char *dst_refname;
- /**
- * The current target of the reference
- */
- git_oid src;
- /**
- * The new target for the reference
- */
- git_oid dst;
-} git_push_update;
-
-/**
- * Callback used to inform of upcoming updates.
- *
- * @param updates an array containing the updates which will be sent
- * as commands to the destination.
- * @param len number of elements in `updates`
- * @param payload Payload provided by the caller
- */
-typedef int (*git_push_negotiation)(const git_push_update **updates, size_t len, void *payload);
-
-/**
- * The callback settings structure
- *
- * Set the callbacks to be called by the remote when informing the user
- * about the progress of the network operations.
- */
-struct git_remote_callbacks {
- unsigned int version;
- /**
- * Textual progress from the remote. Text send over the
- * progress side-band will be passed to this function (this is
- * the 'counting objects' output).
- */
- git_transport_message_cb sideband_progress;
-
- /**
- * Completion is called when different parts of the download
- * process are done (currently unused).
- */
- int (*completion)(git_remote_completion_type type, void *data);
-
- /**
- * This will be called if the remote host requires
- * authentication in order to connect to it.
- *
- * Returning GIT_PASSTHROUGH will make libgit2 behave as
- * though this field isn't set.
- */
- git_cred_acquire_cb credentials;
-
- /**
- * If cert verification fails, this will be called to let the
- * user make the final decision of whether to allow the
- * connection to proceed. Returns 1 to allow the connection, 0
- * to disallow it or a negative value to indicate an error.
- */
- git_transport_certificate_check_cb certificate_check;
-
- /**
- * During the download of new data, this will be regularly
- * called with the current count of progress done by the
- * indexer.
- */
- git_transfer_progress_cb transfer_progress;
-
- /**
- * Each time a reference is updated locally, this function
- * will be called with information about it.
- */
- int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
-
- /**
- * Function to call with progress information during pack
- * building. Be aware that this is called inline with pack
- * building operations, so performance may be affected.
- */
- git_packbuilder_progress pack_progress;
-
- /**
- * Function to call with progress information during the
- * upload portion of a push. Be aware that this is called
- * inline with pack building operations, so performance may be
- * affected.
- */
- git_push_transfer_progress push_transfer_progress;
-
- /**
- * Called for each updated reference on push. If `status` is
- * not `NULL`, the update was rejected by the remote server
- * and `status` contains the reason given.
- */
- int (*push_update_reference)(const char *refname, const char *status, void *data);
-
- /**
- * Called once between the negotiation step and the upload. It
- * provides information about what updates will be performed.
- */
- git_push_negotiation push_negotiation;
-
- /**
- * Create the transport to use for this operation. Leave NULL
- * to auto-detect.
- */
- git_transport_cb transport;
-
- /**
- * This will be passed to each of the callbacks in this struct
- * as the last parameter.
- */
- void *payload;
-};
-
-#define GIT_REMOTE_CALLBACKS_VERSION 1
-#define GIT_REMOTE_CALLBACKS_INIT {GIT_REMOTE_CALLBACKS_VERSION}
-
-/**
- * Initializes a `git_remote_callbacks` with default values. Equivalent to
- * creating an instance with GIT_REMOTE_CALLBACKS_INIT.
- *
- * @param opts the `git_remote_callbacks` struct to initialize
- * @param version Version of struct; pass `GIT_REMOTE_CALLBACKS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_remote_init_callbacks(
- git_remote_callbacks *opts,
- unsigned int version);
-
-typedef enum {
- /**
- * Use the setting from the configuration
- */
- GIT_FETCH_PRUNE_UNSPECIFIED,
- /**
- * Force pruning on
- */
- GIT_FETCH_PRUNE,
- /**
- * Force pruning off
- */
- GIT_FETCH_NO_PRUNE,
-} git_fetch_prune_t;
-
-/**
- * Automatic tag following option
- *
- * Lets us select the --tags option to use.
- */
-typedef enum {
- /**
- * Use the setting from the configuration.
- */
- GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED = 0,
- /**
- * Ask the server for tags pointing to objects we're already
- * downloading.
- */
- GIT_REMOTE_DOWNLOAD_TAGS_AUTO,
- /**
- * Don't ask for any tags beyond the refspecs.
- */
- GIT_REMOTE_DOWNLOAD_TAGS_NONE,
- /**
- * Ask for the all the tags.
- */
- GIT_REMOTE_DOWNLOAD_TAGS_ALL,
-} git_remote_autotag_option_t;
-
-/**
- * Fetch options structure.
- *
- * Zero out for defaults. Initialize with `GIT_FETCH_OPTIONS_INIT` macro to
- * correctly set the `version` field. E.g.
- *
- * git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
- */
-typedef struct {
- int version;
-
- /**
- * Callbacks to use for this fetch operation
- */
- git_remote_callbacks callbacks;
-
- /**
- * Whether to perform a prune after the fetch
- */
- git_fetch_prune_t prune;
-
- /**
- * Whether to write the results to FETCH_HEAD. Defaults to
- * on. Leave this default in order to behave like git.
- */
- int update_fetchhead;
-
- /**
- * Determines how to behave regarding tags on the remote, such
- * as auto-downloading tags for objects we're downloading or
- * downloading all of them.
- *
- * The default is to auto-follow tags.
- */
- git_remote_autotag_option_t download_tags;
-
- /**
- * Proxy options to use, by default no proxy is used.
- */
- git_proxy_options proxy_opts;
-
- /**
- * Extra headers for this fetch operation
- */
- git_strarray custom_headers;
-} git_fetch_options;
-
-#define GIT_FETCH_OPTIONS_VERSION 1
-#define GIT_FETCH_OPTIONS_INIT { GIT_FETCH_OPTIONS_VERSION, GIT_REMOTE_CALLBACKS_INIT, GIT_FETCH_PRUNE_UNSPECIFIED, 1, \
- GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, GIT_PROXY_OPTIONS_INIT }
-
-/**
- * Initializes a `git_fetch_options` with default values. Equivalent to
- * creating an instance with GIT_FETCH_OPTIONS_INIT.
- *
- * @param opts the `git_fetch_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_FETCH_OPTIONS_VERSION` here.
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_fetch_init_options(
- git_fetch_options *opts,
- unsigned int version);
-
-
-/**
- * Controls the behavior of a git_push object.
- */
-typedef struct {
- unsigned int version;
-
- /**
- * If the transport being used to push to the remote requires the creation
- * of a pack file, this controls the number of worker threads used by
- * the packbuilder when creating that pack file to be sent to the remote.
- *
- * If set to 0, the packbuilder will auto-detect the number of threads
- * to create. The default value is 1.
- */
- unsigned int pb_parallelism;
-
- /**
- * Callbacks to use for this push operation
- */
- git_remote_callbacks callbacks;
-
- /**
- * Proxy options to use, by default no proxy is used.
- */
- git_proxy_options proxy_opts;
-
- /**
- * Extra headers for this push operation
- */
- git_strarray custom_headers;
-} git_push_options;
-
-#define GIT_PUSH_OPTIONS_VERSION 1
-#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION, 0, GIT_REMOTE_CALLBACKS_INIT, GIT_PROXY_OPTIONS_INIT }
-
-/**
- * Initializes a `git_push_options` with default values. Equivalent to
- * creating an instance with GIT_PUSH_OPTIONS_INIT.
- *
- * @param opts the `git_push_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_PUSH_OPTIONS_VERSION` here.
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_push_init_options(
- git_push_options *opts,
- unsigned int version);
-
-/**
- * Download and index the packfile
- *
- * Connect to the remote if it hasn't been done yet, negotiate with
- * the remote git which objects are missing, download and index the
- * packfile.
- *
- * The .idx file will be created and both it and the packfile with be
- * renamed to their final name.
- *
- * @param remote the remote
- * @param refspecs the refspecs to use for this negotiation and
- * download. Use NULL or an empty array to use the base refspecs
- * @param opts the options to use for this fetch
- * @return 0 or an error code
- */
- GIT_EXTERN(int) git_remote_download(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts);
-
-/**
- * Create a packfile and send it to the server
- *
- * Connect to the remote if it hasn't been done yet, negotiate with
- * the remote git which objects are missing, create a packfile with the missing objects and send it.
- *
- * @param remote the remote
- * @param refspecs the refspecs to use for this negotiation and
- * upload. Use NULL or an empty array to use the base refspecs
- * @param opts the options to use for this push
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts);
-
-/**
- * Update the tips to the new state
- *
- * @param remote the remote to update
- * @param reflog_message The message to insert into the reflogs. If
- * NULL and fetching, the default is "fetch <name>", where <name> is
- * the name of the remote (or its url, for in-memory remotes). This
- * parameter is ignored when pushing.
- * @param callbacks pointer to the callback structure to use
- * @param update_fetchhead whether to write to FETCH_HEAD. Pass 1 to behave like git.
- * @param download_tags what the behaviour for downloading tags is for this fetch. This is
- * ignored for push. This must be the same value passed to `git_remote_download()`.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_remote_update_tips(
- git_remote *remote,
- const git_remote_callbacks *callbacks,
- int update_fetchhead,
- git_remote_autotag_option_t download_tags,
- const char *reflog_message);
-
-/**
- * Download new data and update tips
- *
- * Convenience function to connect to a remote, download the data,
- * disconnect and update the remote-tracking branches.
- *
- * @param remote the remote to fetch from
- * @param refspecs the refspecs to use for this fetch. Pass NULL or an
- * empty array to use the base refspecs.
- * @param opts options to use for this fetch
- * @param reflog_message The message to insert into the reflogs. If NULL, the
- * default is "fetch"
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_remote_fetch(
- git_remote *remote,
- const git_strarray *refspecs,
- const git_fetch_options *opts,
- const char *reflog_message);
-
-/**
- * Prune tracking refs that are no longer present on remote
- *
- * @param remote the remote to prune
- * @param callbacks callbacks to use for this prune
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks);
-
-/**
- * Perform a push
- *
- * Peform all the steps from a push.
- *
- * @param remote the remote to push to
- * @param refspecs the refspecs to use for pushing. If none are
- * passed, the configured refspecs will be used
- * @param opts options to use for this push
- */
-GIT_EXTERN(int) git_remote_push(git_remote *remote,
- const git_strarray *refspecs,
- const git_push_options *opts);
-
-/**
- * Get the statistics structure that is filled in by the fetch operation.
- */
-GIT_EXTERN(const git_transfer_progress *) git_remote_stats(git_remote *remote);
-
-/**
- * Retrieve the tag auto-follow setting
- *
- * @param remote the remote to query
- * @return the auto-follow setting
- */
-GIT_EXTERN(git_remote_autotag_option_t) git_remote_autotag(const git_remote *remote);
-
-/**
- * Set the remote's tag following setting.
- *
- * The change will be made in the configuration. No loaded remotes
- * will be affected.
- *
- * @param repo the repository in which to make the change
- * @param remote the name of the remote
- * @param value the new value to take.
- */
-GIT_EXTERN(int) git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value);
-/**
- * Retrieve the ref-prune setting
- *
- * @param remote the remote to query
- * @return the ref-prune setting
- */
-GIT_EXTERN(int) git_remote_prune_refs(const git_remote *remote);
-
-/**
- * Give the remote a new name
- *
- * All remote-tracking branches and configuration settings
- * for the remote are updated.
- *
- * The new name will be checked for validity.
- * See `git_tag_create()` for rules about valid names.
- *
- * No loaded instances of a the remote with the old name will change
- * their name or their list of refspecs.
- *
- * @param problems non-default refspecs cannot be renamed and will be
- * stored here for further processing by the caller. Always free this
- * strarray on successful return.
- * @param repo the repository in which to rename
- * @param name the current name of the remote
- * @param new_name the new name the remote should bear
- * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
- */
-GIT_EXTERN(int) git_remote_rename(
- git_strarray *problems,
- git_repository *repo,
- const char *name,
- const char *new_name);
-
-/**
- * Ensure the remote name is well-formed.
- *
- * @param remote_name name to be checked.
- * @return 1 if the reference name is acceptable; 0 if it isn't
- */
-GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name);
-
-/**
-* Delete an existing persisted remote.
-*
-* All remote-tracking branches and configuration settings
-* for the remote will be removed.
-*
-* @param repo the repository in which to act
-* @param name the name of the remove to delete
-* @return 0 on success, or an error code.
-*/
-GIT_EXTERN(int) git_remote_delete(git_repository *repo, const char *name);
-
-/**
- * Retrieve the name of the remote's default branch
- *
- * The default branch of a repository is the branch which HEAD points
- * to. If the remote does not support reporting this information
- * directly, it performs the guess as git does; that is, if there are
- * multiple branches which point to the same commit, the first one is
- * chosen. If the master branch is a candidate, it wins.
- *
- * This function must only be called after connecting.
- *
- * @param out the buffern in which to store the reference name
- * @param remote the remote
- * @return 0, GIT_ENOTFOUND if the remote does not have any references
- * or none of them point to HEAD's commit, or an error message.
- */
-GIT_EXTERN(int) git_remote_default_branch(git_buf *out, git_remote *remote);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_repository_h__
-#define INCLUDE_git_repository_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "buffer.h"
-
-/**
- * @file git2/repository.h
- * @brief Git repository management routines
- * @defgroup git_repository Git repository management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Open a git repository.
- *
- * The 'path' argument must point to either a git repository
- * folder, or an existing work dir.
- *
- * The method will automatically detect if 'path' is a normal
- * or bare repository or fail is 'path' is neither.
- *
- * @param out pointer to the repo which will be opened
- * @param path the path to the repository
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_repository_open(git_repository **out, const char *path);
-
-/**
- * Create a "fake" repository to wrap an object database
- *
- * Create a repository object to wrap an object database to be used
- * with the API when all you have is an object database. This doesn't
- * have any paths associated with it, so use with care.
- *
- * @param out pointer to the repo
- * @param odb the object database to wrap
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_repository_wrap_odb(git_repository **out, git_odb *odb);
-
-/**
- * Look for a git repository and copy its path in the given buffer.
- * The lookup start from base_path and walk across parent directories
- * if nothing has been found. The lookup ends when the first repository
- * is found, or when reaching a directory referenced in ceiling_dirs
- * or when the filesystem changes (in case across_fs is true).
- *
- * The method will automatically detect if the repository is bare
- * (if there is a repository).
- *
- * @param out A pointer to a user-allocated git_buf which will contain
- * the found path.
- *
- * @param start_path The base path where the lookup starts.
- *
- * @param across_fs If true, then the lookup will not stop when a
- * filesystem device change is detected while exploring parent directories.
- *
- * @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR separated list of
- * absolute symbolic link free paths. The lookup will stop when any
- * of this paths is reached. Note that the lookup always performs on
- * start_path no matter start_path appears in ceiling_dirs ceiling_dirs
- * might be NULL (which is equivalent to an empty string)
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_repository_discover(
- git_buf *out,
- const char *start_path,
- int across_fs,
- const char *ceiling_dirs);
-
-/**
- * Option flags for `git_repository_open_ext`.
- *
- * * GIT_REPOSITORY_OPEN_NO_SEARCH - Only open the repository if it can be
- * immediately found in the start_path. Do not walk up from the
- * start_path looking at parent directories.
- * * GIT_REPOSITORY_OPEN_CROSS_FS - Unless this flag is set, open will not
- * continue searching across filesystem boundaries (i.e. when `st_dev`
- * changes from the `stat` system call). (E.g. Searching in a user's home
- * directory "/home/user/source/" will not return "/.git/" as the found
- * repo if "/" is a different filesystem than "/home".)
- * * GIT_REPOSITORY_OPEN_BARE - Open repository as a bare repo regardless
- * of core.bare config, and defer loading config file for faster setup.
- * Unlike `git_repository_open_bare`, this can follow gitlinks.
- * * GIT_REPOSITORY_OPEN_NO_DOTGIT - Do not check for a repository by
- * appending /.git to the start_path; only open the repository if
- * start_path itself points to the git directory.
- * * GIT_REPOSITORY_OPEN_FROM_ENV - Find and open a git repository,
- * respecting the environment variables used by the git command-line
- * tools. If set, `git_repository_open_ext` will ignore the other
- * flags and the `ceiling_dirs` argument, and will allow a NULL `path`
- * to use `GIT_DIR` or search from the current directory. The search
- * for a repository will respect $GIT_CEILING_DIRECTORIES and
- * $GIT_DISCOVERY_ACROSS_FILESYSTEM. The opened repository will
- * respect $GIT_INDEX_FILE, $GIT_NAMESPACE, $GIT_OBJECT_DIRECTORY, and
- * $GIT_ALTERNATE_OBJECT_DIRECTORIES. In the future, this flag will
- * also cause `git_repository_open_ext` to respect $GIT_WORK_TREE and
- * $GIT_COMMON_DIR; currently, `git_repository_open_ext` with this
- * flag will error out if either $GIT_WORK_TREE or $GIT_COMMON_DIR is
- * set.
- */
-typedef enum {
- GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
- GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
- GIT_REPOSITORY_OPEN_BARE = (1 << 2),
- GIT_REPOSITORY_OPEN_NO_DOTGIT = (1 << 3),
- GIT_REPOSITORY_OPEN_FROM_ENV = (1 << 4),
-} git_repository_open_flag_t;
-
-/**
- * Find and open a repository with extended controls.
- *
- * @param out Pointer to the repo which will be opened. This can
- * actually be NULL if you only want to use the error code to
- * see if a repo at this path could be opened.
- * @param path Path to open as git repository. If the flags
- * permit "searching", then this can be a path to a subdirectory
- * inside the working directory of the repository. May be NULL if
- * flags is GIT_REPOSITORY_OPEN_FROM_ENV.
- * @param flags A combination of the GIT_REPOSITORY_OPEN flags above.
- * @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR delimited list of path
- * prefixes at which the search for a containing repository should
- * terminate.
- * @return 0 on success, GIT_ENOTFOUND if no repository could be found,
- * or -1 if there was a repository but open failed for some reason
- * (such as repo corruption or system errors).
- */
-GIT_EXTERN(int) git_repository_open_ext(
- git_repository **out,
- const char *path,
- unsigned int flags,
- const char *ceiling_dirs);
-
-/**
- * Open a bare repository on the serverside.
- *
- * This is a fast open for bare repositories that will come in handy
- * if you're e.g. hosting git repositories and need to access them
- * efficiently
- *
- * @param out Pointer to the repo which will be opened.
- * @param bare_path Direct path to the bare repository
- * @return 0 on success, or an error code
- */
-GIT_EXTERN(int) git_repository_open_bare(git_repository **out, const char *bare_path);
-
-/**
- * Free a previously allocated repository
- *
- * Note that after a repository is free'd, all the objects it has spawned
- * will still exist until they are manually closed by the user
- * with `git_object_free`, but accessing any of the attributes of
- * an object without a backing repository will result in undefined
- * behavior
- *
- * @param repo repository handle to close. If NULL nothing occurs.
- */
-GIT_EXTERN(void) git_repository_free(git_repository *repo);
-
-/**
- * Creates a new Git repository in the given folder.
- *
- * TODO:
- * - Reinit the repository
- *
- * @param out pointer to the repo which will be created or reinitialized
- * @param path the path to the repository
- * @param is_bare if true, a Git repository without a working directory is
- * created at the pointed path. If false, provided path will be
- * considered as the working directory into which the .git directory
- * will be created.
- *
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_repository_init(
- git_repository **out,
- const char *path,
- unsigned is_bare);
-
-/**
- * Option flags for `git_repository_init_ext`.
- *
- * These flags configure extra behaviors to `git_repository_init_ext`.
- * In every case, the default behavior is the zero value (i.e. flag is
- * not set). Just OR the flag values together for the `flags` parameter
- * when initializing a new repo. Details of individual values are:
- *
- * * BARE - Create a bare repository with no working directory.
- * * NO_REINIT - Return an GIT_EEXISTS error if the repo_path appears to
- * already be an git repository.
- * * NO_DOTGIT_DIR - Normally a "/.git/" will be appended to the repo
- * path for non-bare repos (if it is not already there), but
- * passing this flag prevents that behavior.
- * * MKDIR - Make the repo_path (and workdir_path) as needed. Init is
- * always willing to create the ".git" directory even without this
- * flag. This flag tells init to create the trailing component of
- * the repo and workdir paths as needed.
- * * MKPATH - Recursively make all components of the repo and workdir
- * paths as necessary.
- * * EXTERNAL_TEMPLATE - libgit2 normally uses internal templates to
- * initialize a new repo. This flags enables external templates,
- * looking the "template_path" from the options if set, or the
- * `init.templatedir` global config if not, or falling back on
- * "/usr/share/git-core/templates" if it exists.
- * * GIT_REPOSITORY_INIT_RELATIVE_GITLINK - If an alternate workdir is
- * specified, use relative paths for the gitdir and core.worktree.
- */
-typedef enum {
- GIT_REPOSITORY_INIT_BARE = (1u << 0),
- GIT_REPOSITORY_INIT_NO_REINIT = (1u << 1),
- GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = (1u << 2),
- GIT_REPOSITORY_INIT_MKDIR = (1u << 3),
- GIT_REPOSITORY_INIT_MKPATH = (1u << 4),
- GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5),
- GIT_REPOSITORY_INIT_RELATIVE_GITLINK = (1u << 6),
-} git_repository_init_flag_t;
-
-/**
- * Mode options for `git_repository_init_ext`.
- *
- * Set the mode field of the `git_repository_init_options` structure
- * either to the custom mode that you would like, or to one of the
- * following modes:
- *
- * * SHARED_UMASK - Use permissions configured by umask - the default.
- * * SHARED_GROUP - Use "--shared=group" behavior, chmod'ing the new repo
- * to be group writable and "g+sx" for sticky group assignment.
- * * SHARED_ALL - Use "--shared=all" behavior, adding world readability.
- * * Anything else - Set to custom value.
- */
-typedef enum {
- GIT_REPOSITORY_INIT_SHARED_UMASK = 0,
- GIT_REPOSITORY_INIT_SHARED_GROUP = 0002775,
- GIT_REPOSITORY_INIT_SHARED_ALL = 0002777,
-} git_repository_init_mode_t;
-
-/**
- * Extended options structure for `git_repository_init_ext`.
- *
- * This contains extra options for `git_repository_init_ext` that enable
- * additional initialization features. The fields are:
- *
- * * flags - Combination of GIT_REPOSITORY_INIT flags above.
- * * mode - Set to one of the standard GIT_REPOSITORY_INIT_SHARED_...
- * constants above, or to a custom value that you would like.
- * * workdir_path - The path to the working dir or NULL for default (i.e.
- * repo_path parent on non-bare repos). IF THIS IS RELATIVE PATH,
- * IT WILL BE EVALUATED RELATIVE TO THE REPO_PATH. If this is not
- * the "natural" working directory, a .git gitlink file will be
- * created here linking to the repo_path.
- * * description - If set, this will be used to initialize the "description"
- * file in the repository, instead of using the template content.
- * * template_path - When GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE is set,
- * this contains the path to use for the template directory. If
- * this is NULL, the config or default directory options will be
- * used instead.
- * * initial_head - The name of the head to point HEAD at. If NULL, then
- * this will be treated as "master" and the HEAD ref will be set
- * to "refs/heads/master". If this begins with "refs/" it will be
- * used verbatim; otherwise "refs/heads/" will be prefixed.
- * * origin_url - If this is non-NULL, then after the rest of the
- * repository initialization is completed, an "origin" remote
- * will be added pointing to this URL.
- */
-typedef struct {
- unsigned int version;
- uint32_t flags;
- uint32_t mode;
- const char *workdir_path;
- const char *description;
- const char *template_path;
- const char *initial_head;
- const char *origin_url;
-} git_repository_init_options;
-
-#define GIT_REPOSITORY_INIT_OPTIONS_VERSION 1
-#define GIT_REPOSITORY_INIT_OPTIONS_INIT {GIT_REPOSITORY_INIT_OPTIONS_VERSION}
-
-/**
- * Initializes a `git_repository_init_options` with default values. Equivalent
- * to creating an instance with GIT_REPOSITORY_INIT_OPTIONS_INIT.
- *
- * @param opts the `git_repository_init_options` struct to initialize
- * @param version Version of struct; pass `GIT_REPOSITORY_INIT_OPTIONS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_repository_init_init_options(
- git_repository_init_options *opts,
- unsigned int version);
-
-/**
- * Create a new Git repository in the given folder with extended controls.
- *
- * This will initialize a new git repository (creating the repo_path
- * if requested by flags) and working directory as needed. It will
- * auto-detect the case sensitivity of the file system and if the
- * file system supports file mode bits correctly.
- *
- * @param out Pointer to the repo which will be created or reinitialized.
- * @param repo_path The path to the repository.
- * @param opts Pointer to git_repository_init_options struct.
- * @return 0 or an error code on failure.
- */
-GIT_EXTERN(int) git_repository_init_ext(
- git_repository **out,
- const char *repo_path,
- git_repository_init_options *opts);
-
-/**
- * Retrieve and resolve the reference pointed at by HEAD.
- *
- * The returned `git_reference` will be owned by caller and
- * `git_reference_free()` must be called when done with it to release the
- * allocated memory and prevent a leak.
- *
- * @param out pointer to the reference which will be retrieved
- * @param repo a repository object
- *
- * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
- * branch, GIT_ENOTFOUND when HEAD is missing; an error code otherwise
- */
-GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
-
-/**
- * Check if a repository's HEAD is detached
- *
- * A repository's HEAD is detached when it points directly to a commit
- * instead of a branch.
- *
- * @param repo Repo to test
- * @return 1 if HEAD is detached, 0 if it's not; error code if there
- * was an error.
- */
-GIT_EXTERN(int) git_repository_head_detached(git_repository *repo);
-
-/**
- * Check if the current branch is unborn
- *
- * An unborn branch is one named from HEAD but which doesn't exist in
- * the refs namespace, because it doesn't have any commit to point to.
- *
- * @param repo Repo to test
- * @return 1 if the current branch is unborn, 0 if it's not; error
- * code if there was an error
- */
-GIT_EXTERN(int) git_repository_head_unborn(git_repository *repo);
-
-/**
- * Check if a repository is empty
- *
- * An empty repository has just been initialized and contains no references
- * apart from HEAD, which must be pointing to the unborn master branch.
- *
- * @param repo Repo to test
- * @return 1 if the repository is empty, 0 if it isn't, error code
- * if the repository is corrupted
- */
-GIT_EXTERN(int) git_repository_is_empty(git_repository *repo);
-
-/**
- * Get the path of this repository
- *
- * This is the path of the `.git` folder for normal repositories,
- * or of the repository itself for bare repositories.
- *
- * @param repo A repository object
- * @return the path to the repository
- */
-GIT_EXTERN(const char *) git_repository_path(git_repository *repo);
-
-/**
- * Get the path of the working directory for this repository
- *
- * If the repository is bare, this function will always return
- * NULL.
- *
- * @param repo A repository object
- * @return the path to the working dir, if it exists
- */
-GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
-
-/**
- * Set the path to the working directory for this repository
- *
- * The working directory doesn't need to be the same one
- * that contains the `.git` folder for this repository.
- *
- * If this repository is bare, setting its working directory
- * will turn it into a normal repository, capable of performing
- * all the common workdir operations (checkout, status, index
- * manipulation, etc).
- *
- * @param repo A repository object
- * @param workdir The path to a working directory
- * @param update_gitlink Create/update gitlink in workdir and set config
- * "core.worktree" (if workdir is not the parent of the .git directory)
- * @return 0, or an error code
- */
-GIT_EXTERN(int) git_repository_set_workdir(
- git_repository *repo, const char *workdir, int update_gitlink);
-
-/**
- * Check if a repository is bare
- *
- * @param repo Repo to test
- * @return 1 if the repository is bare, 0 otherwise.
- */
-GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
-
-/**
- * Get the configuration file for this repository.
- *
- * If a configuration file has not been set, the default
- * config set for the repository will be returned, including
- * global and system configurations (if they are available).
- *
- * The configuration file must be freed once it's no longer
- * being used by the user.
- *
- * @param out Pointer to store the loaded configuration
- * @param repo A repository object
- * @return 0, or an error code
- */
-GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo);
-
-/**
- * Get a snapshot of the repository's configuration
- *
- * Convenience function to take a snapshot from the repository's
- * configuration. The contents of this snapshot will not change,
- * even if the underlying config files are modified.
- *
- * The configuration file must be freed once it's no longer
- * being used by the user.
- *
- * @param out Pointer to store the loaded configuration
- * @param repo the repository
- * @return 0, or an error code
- */
-GIT_EXTERN(int) git_repository_config_snapshot(git_config **out, git_repository *repo);
-
-/**
- * Get the Object Database for this repository.
- *
- * If a custom ODB has not been set, the default
- * database for the repository will be returned (the one
- * located in `.git/objects`).
- *
- * The ODB must be freed once it's no longer being used by
- * the user.
- *
- * @param out Pointer to store the loaded ODB
- * @param repo A repository object
- * @return 0, or an error code
- */
-GIT_EXTERN(int) git_repository_odb(git_odb **out, git_repository *repo);
-
-/**
- * Get the Reference Database Backend for this repository.
- *
- * If a custom refsdb has not been set, the default database for
- * the repository will be returned (the one that manipulates loose
- * and packed references in the `.git` directory).
- *
- * The refdb must be freed once it's no longer being used by
- * the user.
- *
- * @param out Pointer to store the loaded refdb
- * @param repo A repository object
- * @return 0, or an error code
- */
-GIT_EXTERN(int) git_repository_refdb(git_refdb **out, git_repository *repo);
-
-/**
- * Get the Index file for this repository.
- *
- * If a custom index has not been set, the default
- * index for the repository will be returned (the one
- * located in `.git/index`).
- *
- * The index must be freed once it's no longer being used by
- * the user.
- *
- * @param out Pointer to store the loaded index
- * @param repo A repository object
- * @return 0, or an error code
- */
-GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo);
-
-/**
- * Retrieve git's prepared message
- *
- * Operations such as git revert/cherry-pick/merge with the -n option
- * stop just short of creating a commit with the changes and save
- * their prepared message in .git/MERGE_MSG so the next git-commit
- * execution can present it to the user for them to amend if they
- * wish.
- *
- * Use this function to get the contents of this file. Don't forget to
- * remove the file after you create the commit.
- *
- * @param out git_buf to write data into
- * @param repo Repository to read prepared message from
- * @return 0, GIT_ENOTFOUND if no message exists or an error code
- */
-GIT_EXTERN(int) git_repository_message(git_buf *out, git_repository *repo);
-
-/**
- * Remove git's prepared message.
- *
- * Remove the message that `git_repository_message` retrieves.
- */
-GIT_EXTERN(int) git_repository_message_remove(git_repository *repo);
-
-/**
- * Remove all the metadata associated with an ongoing command like merge,
- * revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc.
- *
- * @param repo A repository object
- * @return 0 on success, or error
- */
-GIT_EXTERN(int) git_repository_state_cleanup(git_repository *repo);
-
-typedef int (*git_repository_fetchhead_foreach_cb)(const char *ref_name,
- const char *remote_url,
- const git_oid *oid,
- unsigned int is_merge,
- void *payload);
-
-/**
- * Invoke 'callback' for each entry in the given FETCH_HEAD file.
- *
- * Return a non-zero value from the callback to stop the loop.
- *
- * @param repo A repository object
- * @param callback Callback function
- * @param payload Pointer to callback data (optional)
- * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if
- * there is no FETCH_HEAD file, or other error code.
- */
-GIT_EXTERN(int) git_repository_fetchhead_foreach(
- git_repository *repo,
- git_repository_fetchhead_foreach_cb callback,
- void *payload);
-
-typedef int (*git_repository_mergehead_foreach_cb)(const git_oid *oid,
- void *payload);
-
-/**
- * If a merge is in progress, invoke 'callback' for each commit ID in the
- * MERGE_HEAD file.
- *
- * Return a non-zero value from the callback to stop the loop.
- *
- * @param repo A repository object
- * @param callback Callback function
- * @param payload Pointer to callback data (optional)
- * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if
- * there is no MERGE_HEAD file, or other error code.
- */
-GIT_EXTERN(int) git_repository_mergehead_foreach(
- git_repository *repo,
- git_repository_mergehead_foreach_cb callback,
- void *payload);
-
-/**
- * Calculate hash of file using repository filtering rules.
- *
- * If you simply want to calculate the hash of a file on disk with no filters,
- * you can just use the `git_odb_hashfile()` API. However, if you want to
- * hash a file in the repository and you want to apply filtering rules (e.g.
- * crlf filters) before generating the SHA, then use this function.
- *
- * Note: if the repository has `core.safecrlf` set to fail and the
- * filtering triggers that failure, then this function will return an
- * error and not calculate the hash of the file.
- *
- * @param out Output value of calculated SHA
- * @param repo Repository pointer
- * @param path Path to file on disk whose contents should be hashed. If the
- * repository is not NULL, this can be a relative path.
- * @param type The object type to hash as (e.g. GIT_OBJ_BLOB)
- * @param as_path The path to use to look up filtering rules. If this is
- * NULL, then the `path` parameter will be used instead. If
- * this is passed as the empty string, then no filters will be
- * applied when calculating the hash.
- * @return 0 on success, or an error code
- */
-GIT_EXTERN(int) git_repository_hashfile(
- git_oid *out,
- git_repository *repo,
- const char *path,
- git_otype type,
- const char *as_path);
-
-/**
- * Make the repository HEAD point to the specified reference.
- *
- * If the provided reference points to a Tree or a Blob, the HEAD is
- * unaltered and -1 is returned.
- *
- * If the provided reference points to a branch, the HEAD will point
- * to that branch, staying attached, or become attached if it isn't yet.
- * If the branch doesn't exist yet, no error will be return. The HEAD
- * will then be attached to an unborn branch.
- *
- * Otherwise, the HEAD will be detached and will directly point to
- * the Commit.
- *
- * @param repo Repository pointer
- * @param refname Canonical name of the reference the HEAD should point at
- * @return 0 on success, or an error code
- */
-GIT_EXTERN(int) git_repository_set_head(
- git_repository* repo,
- const char* refname);
-
-/**
- * Make the repository HEAD directly point to the Commit.
- *
- * If the provided committish cannot be found in the repository, the HEAD
- * is unaltered and GIT_ENOTFOUND is returned.
- *
- * If the provided commitish cannot be peeled into a commit, the HEAD
- * is unaltered and -1 is returned.
- *
- * Otherwise, the HEAD will eventually be detached and will directly point to
- * the peeled Commit.
- *
- * @param repo Repository pointer
- * @param commitish Object id of the Commit the HEAD should point to
- * @return 0 on success, or an error code
- */
-GIT_EXTERN(int) git_repository_set_head_detached(
- git_repository* repo,
- const git_oid* commitish);
-
-/**
- * Make the repository HEAD directly point to the Commit.
- *
- * This behaves like `git_repository_set_head_detached()` but takes an
- * annotated commit, which lets you specify which extended sha syntax
- * string was specified by a user, allowing for more exact reflog
- * messages.
- *
- * See the documentation for `git_repository_set_head_detached()`.
- *
- * @see git_repository_set_head_detached
- */
-GIT_EXTERN(int) git_repository_set_head_detached_from_annotated(
- git_repository *repo,
- const git_annotated_commit *commitish);
-
-/**
- * Detach the HEAD.
- *
- * If the HEAD is already detached and points to a Commit, 0 is returned.
- *
- * If the HEAD is already detached and points to a Tag, the HEAD is
- * updated into making it point to the peeled Commit, and 0 is returned.
- *
- * If the HEAD is already detached and points to a non commitish, the HEAD is
- * unaltered, and -1 is returned.
- *
- * Otherwise, the HEAD will be detached and point to the peeled Commit.
- *
- * @param repo Repository pointer
- * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
- * branch or an error code
- */
-GIT_EXTERN(int) git_repository_detach_head(
- git_repository* repo);
-
-/**
- * Repository state
- *
- * These values represent possible states for the repository to be in,
- * based on the current operation which is ongoing.
- */
-typedef enum {
- GIT_REPOSITORY_STATE_NONE,
- GIT_REPOSITORY_STATE_MERGE,
- GIT_REPOSITORY_STATE_REVERT,
- GIT_REPOSITORY_STATE_REVERT_SEQUENCE,
- GIT_REPOSITORY_STATE_CHERRYPICK,
- GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE,
- GIT_REPOSITORY_STATE_BISECT,
- GIT_REPOSITORY_STATE_REBASE,
- GIT_REPOSITORY_STATE_REBASE_INTERACTIVE,
- GIT_REPOSITORY_STATE_REBASE_MERGE,
- GIT_REPOSITORY_STATE_APPLY_MAILBOX,
- GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE,
-} git_repository_state_t;
-
-/**
- * Determines the status of a git repository - ie, whether an operation
- * (merge, cherry-pick, etc) is in progress.
- *
- * @param repo Repository pointer
- * @return The state of the repository
- */
-GIT_EXTERN(int) git_repository_state(git_repository *repo);
-
-/**
- * Sets the active namespace for this Git Repository
- *
- * This namespace affects all reference operations for the repo.
- * See `man gitnamespaces`
- *
- * @param repo The repo
- * @param nmspace The namespace. This should not include the refs
- * folder, e.g. to namespace all references under `refs/namespaces/foo/`,
- * use `foo` as the namespace.
- * @return 0 on success, -1 on error
- */
-GIT_EXTERN(int) git_repository_set_namespace(git_repository *repo, const char *nmspace);
-
-/**
- * Get the currently active namespace for this repository
- *
- * @param repo The repo
- * @return the active namespace, or NULL if there isn't one
- */
-GIT_EXTERN(const char *) git_repository_get_namespace(git_repository *repo);
-
-
-/**
- * Determine if the repository was a shallow clone
- *
- * @param repo The repository
- * @return 1 if shallow, zero if not
- */
-GIT_EXTERN(int) git_repository_is_shallow(git_repository *repo);
-
-/**
- * Retrieve the configured identity to use for reflogs
- *
- * The memory is owned by the repository and must not be freed by the
- * user.
- *
- * @param name where to store the pointer to the name
- * @param email where to store the pointer to the email
- * @param repo the repository
- */
-GIT_EXTERN(int) git_repository_ident(const char **name, const char **email, const git_repository *repo);
-
-/**
- * Set the identity to be used for writing reflogs
- *
- * If both are set, this name and email will be used to write to the
- * reflog. Pass NULL to unset. When unset, the identity will be taken
- * from the repository's configuration.
- *
- * @param repo the repository to configure
- * @param name the name to use for the reflog entries
- * @param email the email to use for the reflog entries
- */
-GIT_EXTERN(int) git_repository_set_ident(git_repository *repo, const char *name, const char *email);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_reset_h__
-#define INCLUDE_git_reset_h__
-
-#include "common.h"
-#include "types.h"
-#include "strarray.h"
-#include "checkout.h"
-
-/**
- * @file git2/reset.h
- * @brief Git reset management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Kinds of reset operation
- */
-typedef enum {
- GIT_RESET_SOFT = 1, /**< Move the head to the given commit */
- GIT_RESET_MIXED = 2, /**< SOFT plus reset index to the commit */
- GIT_RESET_HARD = 3, /**< MIXED plus changes in working tree discarded */
-} git_reset_t;
-
-/**
- * Sets the current head to the specified commit oid and optionally
- * resets the index and working tree to match.
- *
- * SOFT reset means the Head will be moved to the commit.
- *
- * MIXED reset will trigger a SOFT reset, plus the index will be replaced
- * with the content of the commit tree.
- *
- * HARD reset will trigger a MIXED reset and the working directory will be
- * replaced with the content of the index. (Untracked and ignored files
- * will be left alone, however.)
- *
- * TODO: Implement remaining kinds of resets.
- *
- * @param repo Repository where to perform the reset operation.
- *
- * @param target Committish to which the Head should be moved to. This object
- * must belong to the given `repo` and can either be a git_commit or a
- * git_tag. When a git_tag is being passed, it should be dereferencable
- * to a git_commit which oid will be used as the target of the branch.
- *
- * @param reset_type Kind of reset operation to perform.
- *
- * @param checkout_opts Checkout options to be used for a HARD reset.
- * The checkout_strategy field will be overridden (based on reset_type).
- * This parameter can be used to propagate notify and progress callbacks.
- *
- * @return 0 on success or an error code
- */
-GIT_EXTERN(int) git_reset(
- git_repository *repo,
- git_object *target,
- git_reset_t reset_type,
- const git_checkout_options *checkout_opts);
-
-/**
- * Sets the current head to the specified commit oid and optionally
- * resets the index and working tree to match.
- *
- * This behaves like `git_reset()` but takes an annotated commit,
- * which lets you specify which extended sha syntax string was
- * specified by a user, allowing for more exact reflog messages.
- *
- * See the documentation for `git_reset()`.
- *
- * @see git_reset
- */
-GIT_EXTERN(int) git_reset_from_annotated(
- git_repository *repo,
- git_annotated_commit *commit,
- git_reset_t reset_type,
- const git_checkout_options *checkout_opts);
-
-/**
- * Updates some entries in the index from the target commit tree.
- *
- * The scope of the updated entries is determined by the paths
- * being passed in the `pathspec` parameters.
- *
- * Passing a NULL `target` will result in removing
- * entries in the index matching the provided pathspecs.
- *
- * @param repo Repository where to perform the reset operation.
- *
- * @param target The committish which content will be used to reset the content
- * of the index.
- *
- * @param pathspecs List of pathspecs to operate on.
- *
- * @return 0 on success or an error code < 0
- */
-GIT_EXTERN(int) git_reset_default(
- git_repository *repo,
- git_object *target,
- git_strarray* pathspecs);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_revert_h__
-#define INCLUDE_git_revert_h__
-
-#include "common.h"
-#include "types.h"
-#include "merge.h"
-
-/**
- * @file git2/revert.h
- * @brief Git revert routines
- * @defgroup git_revert Git revert routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Options for revert
- */
-typedef struct {
- unsigned int version;
-
- /** For merge commits, the "mainline" is treated as the parent. */
- unsigned int mainline;
-
- git_merge_options merge_opts; /**< Options for the merging */
- git_checkout_options checkout_opts; /**< Options for the checkout */
-} git_revert_options;
-
-#define GIT_REVERT_OPTIONS_VERSION 1
-#define GIT_REVERT_OPTIONS_INIT {GIT_REVERT_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT}
-
-/**
- * Initializes a `git_revert_options` with default values. Equivalent to
- * creating an instance with GIT_REVERT_OPTIONS_INIT.
- *
- * @param opts the `git_revert_options` struct to initialize
- * @param version Version of struct; pass `GIT_REVERT_OPTIONS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_revert_init_options(
- git_revert_options *opts,
- unsigned int version);
-
-/**
- * Reverts the given commit against the given "our" commit, producing an
- * index that reflects the result of the revert.
- *
- * The returned index must be freed explicitly with `git_index_free`.
- *
- * @param out pointer to store the index result in
- * @param repo the repository that contains the given commits
- * @param revert_commit the commit to revert
- * @param our_commit the commit to revert against (eg, HEAD)
- * @param mainline the parent of the revert commit, if it is a merge
- * @param merge_options the merge options (or null for defaults)
- * @return zero on success, -1 on failure.
- */
-GIT_EXTERN(int) git_revert_commit(
- git_index **out,
- git_repository *repo,
- git_commit *revert_commit,
- git_commit *our_commit,
- unsigned int mainline,
- const git_merge_options *merge_options);
-
-/**
- * Reverts the given commit, producing changes in the index and working directory.
- *
- * @param repo the repository to revert
- * @param commit the commit to revert
- * @param given_opts merge flags
- * @return zero on success, -1 on failure.
- */
-GIT_EXTERN(int) git_revert(
- git_repository *repo,
- git_commit *commit,
- const git_revert_options *given_opts);
-
-/** @} */
-GIT_END_DECL
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_revparse_h__
-#define INCLUDE_git_revparse_h__
-
-#include "common.h"
-#include "types.h"
-
-/**
- * @file git2/revparse.h
- * @brief Git revision parsing routines
- * @defgroup git_revparse Git revision parsing routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Find a single object, as specified by a revision string.
- *
- * See `man gitrevisions`, or
- * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
- * information on the syntax accepted.
- *
- * The returned object should be released with `git_object_free` when no
- * longer needed.
- *
- * @param out pointer to output object
- * @param repo the repository to search in
- * @param spec the textual specification for an object
- * @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC or an error code
- */
-GIT_EXTERN(int) git_revparse_single(
- git_object **out, git_repository *repo, const char *spec);
-
-/**
- * Find a single object and intermediate reference by a revision string.
- *
- * See `man gitrevisions`, or
- * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
- * information on the syntax accepted.
- *
- * In some cases (`@{<-n>}` or `<branchname>@{upstream}`), the expression may
- * point to an intermediate reference. When such expressions are being passed
- * in, `reference_out` will be valued as well.
- *
- * The returned object should be released with `git_object_free` and the
- * returned reference with `git_reference_free` when no longer needed.
- *
- * @param object_out pointer to output object
- * @param reference_out pointer to output reference or NULL
- * @param repo the repository to search in
- * @param spec the textual specification for an object
- * @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC
- * or an error code
- */
-GIT_EXTERN(int) git_revparse_ext(
- git_object **object_out,
- git_reference **reference_out,
- git_repository *repo,
- const char *spec);
-
-/**
- * Revparse flags. These indicate the intended behavior of the spec passed to
- * git_revparse.
- */
-typedef enum {
- /** The spec targeted a single object. */
- GIT_REVPARSE_SINGLE = 1 << 0,
- /** The spec targeted a range of commits. */
- GIT_REVPARSE_RANGE = 1 << 1,
- /** The spec used the '...' operator, which invokes special semantics. */
- GIT_REVPARSE_MERGE_BASE = 1 << 2,
-} git_revparse_mode_t;
-
-/**
- * Git Revision Spec: output of a `git_revparse` operation
- */
-typedef struct {
- /** The left element of the revspec; must be freed by the user */
- git_object *from;
- /** The right element of the revspec; must be freed by the user */
- git_object *to;
- /** The intent of the revspec (i.e. `git_revparse_mode_t` flags) */
- unsigned int flags;
-} git_revspec;
-
-/**
- * Parse a revision string for `from`, `to`, and intent.
- *
- * See `man gitrevisions` or
- * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
- * information on the syntax accepted.
- *
- * @param revspec Pointer to an user-allocated git_revspec struct where
- * the result of the rev-parse will be stored
- * @param repo the repository to search in
- * @param spec the rev-parse spec to parse
- * @return 0 on success, GIT_INVALIDSPEC, GIT_ENOTFOUND, GIT_EAMBIGUOUS or an error code
- */
-GIT_EXTERN(int) git_revparse(
- git_revspec *revspec,
- git_repository *repo,
- const char *spec);
-
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_revwalk_h__
-#define INCLUDE_git_revwalk_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-
-/**
- * @file git2/revwalk.h
- * @brief Git revision traversal routines
- * @defgroup git_revwalk Git revision traversal routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Flags to specify the sorting which a revwalk should perform.
- */
-typedef enum {
- /**
- * Sort the output with the same default time-order method from git.
- * This is the default sorting for new walkers.
- */
- GIT_SORT_NONE = 0,
-
- /**
- * Sort the repository contents in topological order (parents before
- * children); this sorting mode can be combined with time sorting to
- * produce git's "time-order".
- */
- GIT_SORT_TOPOLOGICAL = 1 << 0,
-
- /**
- * Sort the repository contents by commit time;
- * this sorting mode can be combined with
- * topological sorting.
- */
- GIT_SORT_TIME = 1 << 1,
-
- /**
- * Iterate through the repository contents in reverse
- * order; this sorting mode can be combined with
- * any of the above.
- */
- GIT_SORT_REVERSE = 1 << 2,
-} git_sort_t;
-
-/**
- * Allocate a new revision walker to iterate through a repo.
- *
- * This revision walker uses a custom memory pool and an internal
- * commit cache, so it is relatively expensive to allocate.
- *
- * For maximum performance, this revision walker should be
- * reused for different walks.
- *
- * This revision walker is *not* thread safe: it may only be
- * used to walk a repository on a single thread; however,
- * it is possible to have several revision walkers in
- * several different threads walking the same repository.
- *
- * @param out pointer to the new revision walker
- * @param repo the repo to walk through
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_revwalk_new(git_revwalk **out, git_repository *repo);
-
-/**
- * Reset the revision walker for reuse.
- *
- * This will clear all the pushed and hidden commits, and
- * leave the walker in a blank state (just like at
- * creation) ready to receive new commit pushes and
- * start a new walk.
- *
- * The revision walk is automatically reset when a walk
- * is over.
- *
- * @param walker handle to reset.
- */
-GIT_EXTERN(void) git_revwalk_reset(git_revwalk *walker);
-
-/**
- * Add a new root for the traversal
- *
- * The pushed commit will be marked as one of the roots from which to
- * start the walk. This commit may not be walked if it or a child is
- * hidden.
- *
- * At least one commit must be pushed onto the walker before a walk
- * can be started.
- *
- * The given id must belong to a committish on the walked
- * repository.
- *
- * @param walk the walker being used for the traversal.
- * @param id the oid of the commit to start from.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *id);
-
-/**
- * Push matching references
- *
- * The OIDs pointed to by the references that match the given glob
- * pattern will be pushed to the revision walker.
- *
- * A leading 'refs/' is implied if not present as well as a trailing
- * '/\*' if the glob lacks '?', '\*' or '['.
- *
- * Any references matching this glob which do not point to a
- * committish will be ignored.
- *
- * @param walk the walker being used for the traversal
- * @param glob the glob pattern references should match
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_revwalk_push_glob(git_revwalk *walk, const char *glob);
-
-/**
- * Push the repository's HEAD
- *
- * @param walk the walker being used for the traversal
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_revwalk_push_head(git_revwalk *walk);
-
-/**
- * Mark a commit (and its ancestors) uninteresting for the output.
- *
- * The given id must belong to a committish on the walked
- * repository.
- *
- * The resolved commit and all its parents will be hidden from the
- * output on the revision walk.
- *
- * @param walk the walker being used for the traversal.
- * @param commit_id the oid of commit that will be ignored during the traversal
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *commit_id);
-
-/**
- * Hide matching references.
- *
- * The OIDs pointed to by the references that match the given glob
- * pattern and their ancestors will be hidden from the output on the
- * revision walk.
- *
- * A leading 'refs/' is implied if not present as well as a trailing
- * '/\*' if the glob lacks '?', '\*' or '['.
- *
- * Any references matching this glob which do not point to a
- * committish will be ignored.
- *
- * @param walk the walker being used for the traversal
- * @param glob the glob pattern references should match
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_revwalk_hide_glob(git_revwalk *walk, const char *glob);
-
-/**
- * Hide the repository's HEAD
- *
- * @param walk the walker being used for the traversal
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_revwalk_hide_head(git_revwalk *walk);
-
-/**
- * Push the OID pointed to by a reference
- *
- * The reference must point to a committish.
- *
- * @param walk the walker being used for the traversal
- * @param refname the reference to push
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_revwalk_push_ref(git_revwalk *walk, const char *refname);
-
-/**
- * Hide the OID pointed to by a reference
- *
- * The reference must point to a committish.
- *
- * @param walk the walker being used for the traversal
- * @param refname the reference to hide
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_revwalk_hide_ref(git_revwalk *walk, const char *refname);
-
-/**
- * Get the next commit from the revision walk.
- *
- * The initial call to this method is *not* blocking when
- * iterating through a repo with a time-sorting mode.
- *
- * Iterating with Topological or inverted modes makes the initial
- * call blocking to preprocess the commit list, but this block should be
- * mostly unnoticeable on most repositories (topological preprocessing
- * times at 0.3s on the git.git repo).
- *
- * The revision walker is reset when the walk is over.
- *
- * @param out Pointer where to store the oid of the next commit
- * @param walk the walker to pop the commit from.
- * @return 0 if the next commit was found;
- * GIT_ITEROVER if there are no commits left to iterate
- */
-GIT_EXTERN(int) git_revwalk_next(git_oid *out, git_revwalk *walk);
-
-/**
- * Change the sorting mode when iterating through the
- * repository's contents.
- *
- * Changing the sorting mode resets the walker.
- *
- * @param walk the walker being used for the traversal.
- * @param sort_mode combination of GIT_SORT_XXX flags
- */
-GIT_EXTERN(void) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode);
-
-/**
- * Push and hide the respective endpoints of the given range.
- *
- * The range should be of the form
- * <commit>..<commit>
- * where each <commit> is in the form accepted by 'git_revparse_single'.
- * The left-hand commit will be hidden and the right-hand commit pushed.
- *
- * @param walk the walker being used for the traversal
- * @param range the range
- * @return 0 or an error code
- *
- */
-GIT_EXTERN(int) git_revwalk_push_range(git_revwalk *walk, const char *range);
-
-/**
- * Simplify the history by first-parent
- *
- * No parents other than the first for each commit will be enqueued.
- */
-GIT_EXTERN(void) git_revwalk_simplify_first_parent(git_revwalk *walk);
-
-
-/**
- * Free a revision walker previously allocated.
- *
- * @param walk traversal handle to close. If NULL nothing occurs.
- */
-GIT_EXTERN(void) git_revwalk_free(git_revwalk *walk);
-
-/**
- * Return the repository on which this walker
- * is operating.
- *
- * @param walk the revision walker
- * @return the repository being walked
- */
-GIT_EXTERN(git_repository *) git_revwalk_repository(git_revwalk *walk);
-
-/**
- * This is a callback function that user can provide to hide a
- * commit and its parents. If the callback function returns non-zero value,
- * then this commit and its parents will be hidden.
- *
- * @param commit_id oid of Commit
- * @param payload User-specified pointer to data to be passed as data payload
- */
-typedef int(*git_revwalk_hide_cb)(
- const git_oid *commit_id,
- void *payload);
-
-/**
- * Adds a callback function to hide a commit and its parents
- *
- * @param walk the revision walker
- * @param hide_cb callback function to hide a commit and its parents
- * @param payload data payload to be passed to callback function
- */
-GIT_EXTERN(int) git_revwalk_add_hide_cb(
- git_revwalk *walk,
- git_revwalk_hide_cb hide_cb,
- void *payload);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_signature_h__
-#define INCLUDE_git_signature_h__
-
-#include "common.h"
-#include "types.h"
-
-/**
- * @file git2/signature.h
- * @brief Git signature creation
- * @defgroup git_signature Git signature creation
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Create a new action signature.
- *
- * Call `git_signature_free()` to free the data.
- *
- * Note: angle brackets ('<' and '>') characters are not allowed
- * to be used in either the `name` or the `email` parameter.
- *
- * @param out new signature, in case of error NULL
- * @param name name of the person
- * @param email email of the person
- * @param time time when the action happened
- * @param offset timezone offset in minutes for the time
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_signature_new(git_signature **out, const char *name, const char *email, git_time_t time, int offset);
-
-/**
- * Create a new action signature with a timestamp of 'now'.
- *
- * Call `git_signature_free()` to free the data.
- *
- * @param out new signature, in case of error NULL
- * @param name name of the person
- * @param email email of the person
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const char *email);
-
-/**
- * Create a new action signature with default user and now timestamp.
- *
- * This looks up the user.name and user.email from the configuration and
- * uses the current time as the timestamp, and creates a new signature
- * based on that information. It will return GIT_ENOTFOUND if either the
- * user.name or user.email are not set.
- *
- * @param out new signature
- * @param repo repository pointer
- * @return 0 on success, GIT_ENOTFOUND if config is missing, or error code
- */
-GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo);
-
-/**
- * Create a new signature by parsing the given buffer, which is
- * expected to be in the format "Real Name <email> timestamp tzoffset",
- * where `timestamp` is the number of seconds since the Unix epoch and
- * `tzoffset` is the timezone offset in `hhmm` format (note the lack
- * of a colon separator).
- *
- * @param out new signature
- * @param buf signature string
- * @return 0 on success, or an error code
- */
-GIT_EXTERN(int) git_signature_from_buffer(git_signature **out, const char *buf);
-
-/**
- * Create a copy of an existing signature. All internal strings are also
- * duplicated.
- *
- * Call `git_signature_free()` to free the data.
- *
- * @param dest pointer where to store the copy
- * @param sig signature to duplicate
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_signature_dup(git_signature **dest, const git_signature *sig);
-
-/**
- * Free an existing signature.
- *
- * Because the signature is not an opaque structure, it is legal to free it
- * manually, but be sure to free the "name" and "email" strings in addition
- * to the structure itself.
- *
- * @param sig signature to free
- */
-GIT_EXTERN(void) git_signature_free(git_signature *sig);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_stash_h__
-#define INCLUDE_git_stash_h__
-
-#include "common.h"
-#include "types.h"
-
-/**
- * @file git2/stash.h
- * @brief Git stash management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Stash flags
- */
-typedef enum {
- /**
- * No option, default
- */
- GIT_STASH_DEFAULT = 0,
-
- /**
- * All changes already added to the index are left intact in
- * the working directory
- */
- GIT_STASH_KEEP_INDEX = (1 << 0),
-
- /**
- * All untracked files are also stashed and then cleaned up
- * from the working directory
- */
- GIT_STASH_INCLUDE_UNTRACKED = (1 << 1),
-
- /**
- * All ignored files are also stashed and then cleaned up from
- * the working directory
- */
- GIT_STASH_INCLUDE_IGNORED = (1 << 2),
-} git_stash_flags;
-
-/**
- * Save the local modifications to a new stash.
- *
- * @param out Object id of the commit containing the stashed state.
- * This commit is also the target of the direct reference refs/stash.
- *
- * @param repo The owning repository.
- *
- * @param stasher The identity of the person performing the stashing.
- *
- * @param message Optional description along with the stashed state.
- *
- * @param flags Flags to control the stashing process. (see GIT_STASH_* above)
- *
- * @return 0 on success, GIT_ENOTFOUND where there's nothing to stash,
- * or error code.
- */
-GIT_EXTERN(int) git_stash_save(
- git_oid *out,
- git_repository *repo,
- const git_signature *stasher,
- const char *message,
- uint32_t flags);
-
-/** Stash application flags. */
-typedef enum {
- GIT_STASH_APPLY_DEFAULT = 0,
-
- /* Try to reinstate not only the working tree's changes,
- * but also the index's changes.
- */
- GIT_STASH_APPLY_REINSTATE_INDEX = (1 << 0),
-} git_stash_apply_flags;
-
-typedef enum {
- GIT_STASH_APPLY_PROGRESS_NONE = 0,
-
- /** Loading the stashed data from the object database. */
- GIT_STASH_APPLY_PROGRESS_LOADING_STASH,
-
- /** The stored index is being analyzed. */
- GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX,
-
- /** The modified files are being analyzed. */
- GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED,
-
- /** The untracked and ignored files are being analyzed. */
- GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED,
-
- /** The untracked files are being written to disk. */
- GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED,
-
- /** The modified files are being written to disk. */
- GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED,
-
- /** The stash was applied successfully. */
- GIT_STASH_APPLY_PROGRESS_DONE,
-} git_stash_apply_progress_t;
-
-/**
- * Stash application progress notification function.
- * Return 0 to continue processing, or a negative value to
- * abort the stash application.
- */
-typedef int (*git_stash_apply_progress_cb)(
- git_stash_apply_progress_t progress,
- void *payload);
-
-/** Stash application options structure.
- *
- * Initialize with the `GIT_STASH_APPLY_OPTIONS_INIT` macro to set
- * sensible defaults; for example:
- *
- * git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
- */
-typedef struct git_stash_apply_options {
- unsigned int version;
-
- /** See `git_stash_apply_flags_t`, above. */
- git_stash_apply_flags flags;
-
- /** Options to use when writing files to the working directory. */
- git_checkout_options checkout_options;
-
- /** Optional callback to notify the consumer of application progress. */
- git_stash_apply_progress_cb progress_cb;
- void *progress_payload;
-} git_stash_apply_options;
-
-#define GIT_STASH_APPLY_OPTIONS_VERSION 1
-#define GIT_STASH_APPLY_OPTIONS_INIT { \
- GIT_STASH_APPLY_OPTIONS_VERSION, \
- GIT_STASH_APPLY_DEFAULT, \
- GIT_CHECKOUT_OPTIONS_INIT }
-
-/**
- * Initializes a `git_stash_apply_options` with default values. Equivalent to
- * creating an instance with GIT_STASH_APPLY_OPTIONS_INIT.
- *
- * @param opts the `git_stash_apply_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_STASH_APPLY_OPTIONS_INIT` here.
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_stash_apply_init_options(
- git_stash_apply_options *opts, unsigned int version);
-
-/**
- * Apply a single stashed state from the stash list.
- *
- * If local changes in the working directory conflict with changes in the
- * stash then GIT_EMERGECONFLICT will be returned. In this case, the index
- * will always remain unmodified and all files in the working directory will
- * remain unmodified. However, if you are restoring untracked files or
- * ignored files and there is a conflict when applying the modified files,
- * then those files will remain in the working directory.
- *
- * If passing the GIT_STASH_APPLY_REINSTATE_INDEX flag and there would be
- * conflicts when reinstating the index, the function will return
- * GIT_EMERGECONFLICT and both the working directory and index will be left
- * unmodified.
- *
- * Note that a minimum checkout strategy of `GIT_CHECKOUT_SAFE` is implied.
- *
- * @param repo The owning repository.
- * @param index The position within the stash list. 0 points to the
- * most recent stashed state.
- * @param options Options to control how stashes are applied.
- *
- * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the
- * given index, GIT_EMERGECONFLICT if changes exist in the working
- * directory, or an error code
- */
-GIT_EXTERN(int) git_stash_apply(
- git_repository *repo,
- size_t index,
- const git_stash_apply_options *options);
-
-/**
- * This is a callback function you can provide to iterate over all the
- * stashed states that will be invoked per entry.
- *
- * @param index The position within the stash list. 0 points to the
- * most recent stashed state.
- * @param message The stash message.
- * @param stash_id The commit oid of the stashed state.
- * @param payload Extra parameter to callback function.
- * @return 0 to continue iterating or non-zero to stop.
- */
-typedef int (*git_stash_cb)(
- size_t index,
- const char* message,
- const git_oid *stash_id,
- void *payload);
-
-/**
- * Loop over all the stashed states and issue a callback for each one.
- *
- * If the callback returns a non-zero value, this will stop looping.
- *
- * @param repo Repository where to find the stash.
- *
- * @param callback Callback to invoke per found stashed state. The most
- * recent stash state will be enumerated first.
- *
- * @param payload Extra parameter to callback function.
- *
- * @return 0 on success, non-zero callback return value, or error code.
- */
-GIT_EXTERN(int) git_stash_foreach(
- git_repository *repo,
- git_stash_cb callback,
- void *payload);
-
-/**
- * Remove a single stashed state from the stash list.
- *
- * @param repo The owning repository.
- *
- * @param index The position within the stash list. 0 points to the
- * most recent stashed state.
- *
- * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the given
- * index, or error code.
- */
-GIT_EXTERN(int) git_stash_drop(
- git_repository *repo,
- size_t index);
-
-/**
- * Apply a single stashed state from the stash list and remove it from the list
- * if successful.
- *
- * @param repo The owning repository.
- * @param index The position within the stash list. 0 points to the
- * most recent stashed state.
- * @param options Options to control how stashes are applied.
- *
- * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the given
- * index, or error code. (see git_stash_apply() above for details)
-*/
-GIT_EXTERN(int) git_stash_pop(
- git_repository *repo,
- size_t index,
- const git_stash_apply_options *options);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_status_h__
-#define INCLUDE_git_status_h__
-
-#include "common.h"
-#include "types.h"
-
-/**
- * @file git2/status.h
- * @brief Git file status routines
- * @defgroup git_status Git file status routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Status flags for a single file.
- *
- * A combination of these values will be returned to indicate the status of
- * a file. Status compares the working directory, the index, and the
- * current HEAD of the repository. The `GIT_STATUS_INDEX` set of flags
- * represents the status of file in the index relative to the HEAD, and the
- * `GIT_STATUS_WT` set of flags represent the status of the file in the
- * working directory relative to the index.
- */
-typedef enum {
- GIT_STATUS_CURRENT = 0,
-
- GIT_STATUS_INDEX_NEW = (1u << 0),
- GIT_STATUS_INDEX_MODIFIED = (1u << 1),
- GIT_STATUS_INDEX_DELETED = (1u << 2),
- GIT_STATUS_INDEX_RENAMED = (1u << 3),
- GIT_STATUS_INDEX_TYPECHANGE = (1u << 4),
-
- GIT_STATUS_WT_NEW = (1u << 7),
- GIT_STATUS_WT_MODIFIED = (1u << 8),
- GIT_STATUS_WT_DELETED = (1u << 9),
- GIT_STATUS_WT_TYPECHANGE = (1u << 10),
- GIT_STATUS_WT_RENAMED = (1u << 11),
- GIT_STATUS_WT_UNREADABLE = (1u << 12),
-
- GIT_STATUS_IGNORED = (1u << 14),
- GIT_STATUS_CONFLICTED = (1u << 15),
-} git_status_t;
-
-/**
- * Function pointer to receive status on individual files
- *
- * `path` is the relative path to the file from the root of the repository.
- *
- * `status_flags` is a combination of `git_status_t` values that apply.
- *
- * `payload` is the value you passed to the foreach function as payload.
- */
-typedef int (*git_status_cb)(
- const char *path, unsigned int status_flags, void *payload);
-
-/**
- * Select the files on which to report status.
- *
- * With `git_status_foreach_ext`, this will control which changes get
- * callbacks. With `git_status_list_new`, these will control which
- * changes are included in the list.
- *
- * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This roughly
- * matches `git status --porcelain` regarding which files are
- * included and in what order.
- * - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index
- * comparison, not looking at working directory changes.
- * - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to
- * working directory comparison, not comparing the index to the HEAD.
- */
-typedef enum {
- GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
- GIT_STATUS_SHOW_INDEX_ONLY = 1,
- GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
-} git_status_show_t;
-
-/**
- * Flags to control status callbacks
- *
- * - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
- * on untracked files. These will only be made if the workdir files are
- * included in the status "show" option.
- * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files get callbacks.
- * Again, these callbacks will only be made if the workdir files are
- * included in the status "show" option.
- * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
- * made even on unmodified files.
- * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that submodules should be
- * skipped. This only applies if there are no pending typechanges to
- * the submodule (either from or to another type).
- * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that all files in
- * untracked directories should be included. Normally if an entire
- * directory is new, then just the top-level directory is included (with
- * a trailing slash on the entry name). This flag says to include all
- * of the individual files in the directory instead.
- * - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given path
- * should be treated as a literal path, and not as a pathspec pattern.
- * - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of
- * ignored directories should be included in the status. This is like
- * doing `git ls-files -o -i --exclude-standard` with core git.
- * - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that rename detection
- * should be processed between the head and the index and enables
- * the GIT_STATUS_INDEX_RENAMED as a possible status flag.
- * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates that rename
- * detection should be run between the index and the working directory
- * and enabled GIT_STATUS_WT_RENAMED as a possible status flag.
- * - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY overrides the native case
- * sensitivity for the file system and forces the output to be in
- * case-sensitive order
- * - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY overrides the native case
- * sensitivity for the file system and forces the output to be in
- * case-insensitive order
- * - GIT_STATUS_OPT_RENAMES_FROM_REWRITES indicates that rename detection
- * should include rewritten files
- * - GIT_STATUS_OPT_NO_REFRESH bypasses the default status behavior of
- * doing a "soft" index reload (i.e. reloading the index data if the
- * file on disk has been modified outside libgit2).
- * - GIT_STATUS_OPT_UPDATE_INDEX tells libgit2 to refresh the stat cache
- * in the index for files that are unchanged but have out of date stat
- * information in the index. It will result in less work being done on
- * subsequent calls to get status. This is mutually exclusive with the
- * NO_REFRESH option.
- *
- * Calling `git_status_foreach()` is like calling the extended version
- * with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
- * and GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS. Those options are bundled
- * together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
- */
-typedef enum {
- GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
- GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
- GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
- GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
- GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
- GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
- GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
- GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
- GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
- GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
- GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
- GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
- GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
- GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13),
- GIT_STATUS_OPT_INCLUDE_UNREADABLE = (1u << 14),
- GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 15),
-} git_status_opt_t;
-
-#define GIT_STATUS_OPT_DEFAULTS \
- (GIT_STATUS_OPT_INCLUDE_IGNORED | \
- GIT_STATUS_OPT_INCLUDE_UNTRACKED | \
- GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS)
-
-/**
- * Options to control how `git_status_foreach_ext()` will issue callbacks.
- *
- * This structure is set so that zeroing it out will give you relatively
- * sane defaults.
- *
- * The `show` value is one of the `git_status_show_t` constants that
- * control which files to scan and in what order.
- *
- * The `flags` value is an OR'ed combination of the `git_status_opt_t`
- * values above.
- *
- * The `pathspec` is an array of path patterns to match (using
- * fnmatch-style matching), or just an array of paths to match exactly if
- * `GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH` is specified in the flags.
- */
-typedef struct {
- unsigned int version;
- git_status_show_t show;
- unsigned int flags;
- git_strarray pathspec;
-} git_status_options;
-
-#define GIT_STATUS_OPTIONS_VERSION 1
-#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION}
-
-/**
- * Initializes a `git_status_options` with default values. Equivalent to
- * creating an instance with GIT_STATUS_OPTIONS_INIT.
- *
- * @param opts The `git_status_options` instance to initialize.
- * @param version Version of struct; pass `GIT_STATUS_OPTIONS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_status_init_options(
- git_status_options *opts,
- unsigned int version);
-
-/**
- * A status entry, providing the differences between the file as it exists
- * in HEAD and the index, and providing the differences between the index
- * and the working directory.
- *
- * The `status` value provides the status flags for this file.
- *
- * The `head_to_index` value provides detailed information about the
- * differences between the file in HEAD and the file in the index.
- *
- * The `index_to_workdir` value provides detailed information about the
- * differences between the file in the index and the file in the
- * working directory.
- */
-typedef struct {
- git_status_t status;
- git_diff_delta *head_to_index;
- git_diff_delta *index_to_workdir;
-} git_status_entry;
-
-
-/**
- * Gather file statuses and run a callback for each one.
- *
- * The callback is passed the path of the file, the status (a combination of
- * the `git_status_t` values above) and the `payload` data pointer passed
- * into this function.
- *
- * If the callback returns a non-zero value, this function will stop looping
- * and return that value to caller.
- *
- * @param repo A repository object
- * @param callback The function to call on each file
- * @param payload Pointer to pass through to callback function
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_status_foreach(
- git_repository *repo,
- git_status_cb callback,
- void *payload);
-
-/**
- * Gather file status information and run callbacks as requested.
- *
- * This is an extended version of the `git_status_foreach()` API that
- * allows for more granular control over which paths will be processed and
- * in what order. See the `git_status_options` structure for details
- * about the additional controls that this makes available.
- *
- * Note that if a `pathspec` is given in the `git_status_options` to filter
- * the status, then the results from rename detection (if you enable it) may
- * not be accurate. To do rename detection properly, this must be called
- * with no `pathspec` so that all files can be considered.
- *
- * @param repo Repository object
- * @param opts Status options structure
- * @param callback The function to call on each file
- * @param payload Pointer to pass through to callback function
- * @return 0 on success, non-zero callback return value, or error code
- */
-GIT_EXTERN(int) git_status_foreach_ext(
- git_repository *repo,
- const git_status_options *opts,
- git_status_cb callback,
- void *payload);
-
-/**
- * Get file status for a single file.
- *
- * This tries to get status for the filename that you give. If no files
- * match that name (in either the HEAD, index, or working directory), this
- * returns GIT_ENOTFOUND.
- *
- * If the name matches multiple files (for example, if the `path` names a
- * directory or if running on a case- insensitive filesystem and yet the
- * HEAD has two entries that both match the path), then this returns
- * GIT_EAMBIGUOUS because it cannot give correct results.
- *
- * This does not do any sort of rename detection. Renames require a set of
- * targets and because of the path filtering, there is not enough
- * information to check renames correctly. To check file status with rename
- * detection, there is no choice but to do a full `git_status_list_new` and
- * scan through looking for the path that you are interested in.
- *
- * @param status_flags Output combination of git_status_t values for file
- * @param repo A repository object
- * @param path The exact path to retrieve status for relative to the
- * repository working directory
- * @return 0 on success, GIT_ENOTFOUND if the file is not found in the HEAD,
- * index, and work tree, GIT_EAMBIGUOUS if `path` matches multiple files
- * or if it refers to a folder, and -1 on other errors.
- */
-GIT_EXTERN(int) git_status_file(
- unsigned int *status_flags,
- git_repository *repo,
- const char *path);
-
-/**
- * Gather file status information and populate the `git_status_list`.
- *
- * Note that if a `pathspec` is given in the `git_status_options` to filter
- * the status, then the results from rename detection (if you enable it) may
- * not be accurate. To do rename detection properly, this must be called
- * with no `pathspec` so that all files can be considered.
- *
- * @param out Pointer to store the status results in
- * @param repo Repository object
- * @param opts Status options structure
- * @return 0 on success or error code
- */
-GIT_EXTERN(int) git_status_list_new(
- git_status_list **out,
- git_repository *repo,
- const git_status_options *opts);
-
-/**
- * Gets the count of status entries in this list.
- *
- * If there are no changes in status (at least according the options given
- * when the status list was created), this can return 0.
- *
- * @param statuslist Existing status list object
- * @return the number of status entries
- */
-GIT_EXTERN(size_t) git_status_list_entrycount(
- git_status_list *statuslist);
-
-/**
- * Get a pointer to one of the entries in the status list.
- *
- * The entry is not modifiable and should not be freed.
- *
- * @param statuslist Existing status list object
- * @param idx Position of the entry
- * @return Pointer to the entry; NULL if out of bounds
- */
-GIT_EXTERN(const git_status_entry *) git_status_byindex(
- git_status_list *statuslist,
- size_t idx);
-
-/**
- * Free an existing status list
- *
- * @param statuslist Existing status list object
- */
-GIT_EXTERN(void) git_status_list_free(
- git_status_list *statuslist);
-
-/**
- * Test if the ignore rules apply to a given file.
- *
- * This function checks the ignore rules to see if they would apply to the
- * given file. This indicates if the file would be ignored regardless of
- * whether the file is already in the index or committed to the repository.
- *
- * One way to think of this is if you were to do "git add ." on the
- * directory containing the file, would it be added or not?
- *
- * @param ignored Boolean returning 0 if the file is not ignored, 1 if it is
- * @param repo A repository object
- * @param path The file to check ignores for, rooted at the repo's workdir.
- * @return 0 if ignore rules could be processed for the file (regardless
- * of whether it exists or not), or an error < 0 if they could not.
- */
-GIT_EXTERN(int) git_status_should_ignore(
- int *ignored,
- git_repository *repo,
- const char *path);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-// ISO C9x compliant stdint.h for Microsoft Visual Studio
-// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
-//
-// Copyright (c) 2006-2008 Alexander Chemeris
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// 3. The name of the author may be used to endorse or promote products
-// derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef _MSC_VER // [
-#error "Use this header only with Microsoft Visual C++ compilers!"
-#endif // _MSC_VER ]
-
-#ifndef _MSC_STDINT_H_ // [
-#define _MSC_STDINT_H_
-
-#if _MSC_VER > 1000
-#pragma once
-#endif
-
-#include <limits.h>
-
-// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
-// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
-// or compiler give many errors like this:
-// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
-#ifdef __cplusplus
-extern "C" {
-#endif
-# include <wchar.h>
-#ifdef __cplusplus
-}
-#endif
-
-// Define _W64 macros to mark types changing their size, like intptr_t.
-#ifndef _W64
-# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
-# define _W64 __w64
-# else
-# define _W64
-# endif
-#endif
-
-
-// 7.18.1 Integer types
-
-// 7.18.1.1 Exact-width integer types
-
-// Visual Studio 6 and Embedded Visual C++ 4 doesn't
-// realize that, e.g. char has the same size as __int8
-// so we give up on __intX for them.
-#if (_MSC_VER < 1300)
- typedef signed char int8_t;
- typedef signed short int16_t;
- typedef signed int int32_t;
- typedef unsigned char uint8_t;
- typedef unsigned short uint16_t;
- typedef unsigned int uint32_t;
-#else
- typedef signed __int8 int8_t;
- typedef signed __int16 int16_t;
- typedef signed __int32 int32_t;
- typedef unsigned __int8 uint8_t;
- typedef unsigned __int16 uint16_t;
- typedef unsigned __int32 uint32_t;
-#endif
-typedef signed __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-
-
-// 7.18.1.2 Minimum-width integer types
-typedef int8_t int_least8_t;
-typedef int16_t int_least16_t;
-typedef int32_t int_least32_t;
-typedef int64_t int_least64_t;
-typedef uint8_t uint_least8_t;
-typedef uint16_t uint_least16_t;
-typedef uint32_t uint_least32_t;
-typedef uint64_t uint_least64_t;
-
-// 7.18.1.3 Fastest minimum-width integer types
-typedef int8_t int_fast8_t;
-typedef int16_t int_fast16_t;
-typedef int32_t int_fast32_t;
-typedef int64_t int_fast64_t;
-typedef uint8_t uint_fast8_t;
-typedef uint16_t uint_fast16_t;
-typedef uint32_t uint_fast32_t;
-typedef uint64_t uint_fast64_t;
-
-// 7.18.1.4 Integer types capable of holding object pointers
-#ifdef _WIN64 // [
- typedef signed __int64 intptr_t;
- typedef unsigned __int64 uintptr_t;
-#else // _WIN64 ][
- typedef _W64 signed int intptr_t;
- typedef _W64 unsigned int uintptr_t;
-#endif // _WIN64 ]
-
-// 7.18.1.5 Greatest-width integer types
-typedef int64_t intmax_t;
-typedef uint64_t uintmax_t;
-
-
-// 7.18.2 Limits of specified-width integer types
-
-#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
-
-// 7.18.2.1 Limits of exact-width integer types
-#define INT8_MIN ((int8_t)_I8_MIN)
-#define INT8_MAX _I8_MAX
-#define INT16_MIN ((int16_t)_I16_MIN)
-#define INT16_MAX _I16_MAX
-#define INT32_MIN ((int32_t)_I32_MIN)
-#define INT32_MAX _I32_MAX
-#define INT64_MIN ((int64_t)_I64_MIN)
-#define INT64_MAX _I64_MAX
-#define UINT8_MAX _UI8_MAX
-#define UINT16_MAX _UI16_MAX
-#define UINT32_MAX _UI32_MAX
-#define UINT64_MAX _UI64_MAX
-
-// 7.18.2.2 Limits of minimum-width integer types
-#define INT_LEAST8_MIN INT8_MIN
-#define INT_LEAST8_MAX INT8_MAX
-#define INT_LEAST16_MIN INT16_MIN
-#define INT_LEAST16_MAX INT16_MAX
-#define INT_LEAST32_MIN INT32_MIN
-#define INT_LEAST32_MAX INT32_MAX
-#define INT_LEAST64_MIN INT64_MIN
-#define INT_LEAST64_MAX INT64_MAX
-#define UINT_LEAST8_MAX UINT8_MAX
-#define UINT_LEAST16_MAX UINT16_MAX
-#define UINT_LEAST32_MAX UINT32_MAX
-#define UINT_LEAST64_MAX UINT64_MAX
-
-// 7.18.2.3 Limits of fastest minimum-width integer types
-#define INT_FAST8_MIN INT8_MIN
-#define INT_FAST8_MAX INT8_MAX
-#define INT_FAST16_MIN INT16_MIN
-#define INT_FAST16_MAX INT16_MAX
-#define INT_FAST32_MIN INT32_MIN
-#define INT_FAST32_MAX INT32_MAX
-#define INT_FAST64_MIN INT64_MIN
-#define INT_FAST64_MAX INT64_MAX
-#define UINT_FAST8_MAX UINT8_MAX
-#define UINT_FAST16_MAX UINT16_MAX
-#define UINT_FAST32_MAX UINT32_MAX
-#define UINT_FAST64_MAX UINT64_MAX
-
-// 7.18.2.4 Limits of integer types capable of holding object pointers
-#ifdef _WIN64 // [
-# define INTPTR_MIN INT64_MIN
-# define INTPTR_MAX INT64_MAX
-# define UINTPTR_MAX UINT64_MAX
-#else // _WIN64 ][
-# define INTPTR_MIN INT32_MIN
-# define INTPTR_MAX INT32_MAX
-# define UINTPTR_MAX UINT32_MAX
-#endif // _WIN64 ]
-
-// 7.18.2.5 Limits of greatest-width integer types
-#define INTMAX_MIN INT64_MIN
-#define INTMAX_MAX INT64_MAX
-#define UINTMAX_MAX UINT64_MAX
-
-// 7.18.3 Limits of other integer types
-
-#ifdef _WIN64 // [
-# define PTRDIFF_MIN _I64_MIN
-# define PTRDIFF_MAX _I64_MAX
-#else // _WIN64 ][
-# define PTRDIFF_MIN _I32_MIN
-# define PTRDIFF_MAX _I32_MAX
-#endif // _WIN64 ]
-
-#define SIG_ATOMIC_MIN INT_MIN
-#define SIG_ATOMIC_MAX INT_MAX
-
-#ifndef SIZE_MAX // [
-# ifdef _WIN64 // [
-# define SIZE_MAX _UI64_MAX
-# else // _WIN64 ][
-# define SIZE_MAX _UI32_MAX
-# endif // _WIN64 ]
-#endif // SIZE_MAX ]
-
-// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
-#ifndef WCHAR_MIN // [
-# define WCHAR_MIN 0
-#endif // WCHAR_MIN ]
-#ifndef WCHAR_MAX // [
-# define WCHAR_MAX _UI16_MAX
-#endif // WCHAR_MAX ]
-
-#define WINT_MIN 0
-#define WINT_MAX _UI16_MAX
-
-#endif // __STDC_LIMIT_MACROS ]
-
-
-// 7.18.4 Limits of other integer types
-
-#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
-
-// 7.18.4.1 Macros for minimum-width integer constants
-
-#define INT8_C(val) val##i8
-#define INT16_C(val) val##i16
-#define INT32_C(val) val##i32
-#define INT64_C(val) val##i64
-
-#define UINT8_C(val) val##ui8
-#define UINT16_C(val) val##ui16
-#define UINT32_C(val) val##ui32
-#define UINT64_C(val) val##ui64
-
-// 7.18.4.2 Macros for greatest-width integer constants
-#define INTMAX_C INT64_C
-#define UINTMAX_C UINT64_C
-
-#endif // __STDC_CONSTANT_MACROS ]
-
-
-#endif // _MSC_STDINT_H_ ]
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_strarray_h__
-#define INCLUDE_git_strarray_h__
-
-#include "common.h"
-
-/**
- * @file git2/strarray.h
- * @brief Git string array routines
- * @defgroup git_strarray Git string array routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/** Array of strings */
-typedef struct git_strarray {
- char **strings;
- size_t count;
-} git_strarray;
-
-/**
- * Close a string array object
- *
- * This method should be called on `git_strarray` objects where the strings
- * array is allocated and contains allocated strings, such as what you
- * would get from `git_strarray_copy()`. Not doing so, will result in a
- * memory leak.
- *
- * This does not free the `git_strarray` itself, since the library will
- * never allocate that object directly itself (it is more commonly embedded
- * inside another struct or created on the stack).
- *
- * @param array git_strarray from which to free string data
- */
-GIT_EXTERN(void) git_strarray_free(git_strarray *array);
-
-/**
- * Copy a string array object from source to target.
- *
- * Note: target is overwritten and hence should be empty, otherwise its
- * contents are leaked. Call git_strarray_free() if necessary.
- *
- * @param tgt target
- * @param src source
- * @return 0 on success, < 0 on allocation failure
- */
-GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src);
-
-
-/** @} */
-GIT_END_DECL
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_submodule_h__
-#define INCLUDE_git_submodule_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "remote.h"
-#include "checkout.h"
-
-/**
- * @file git2/submodule.h
- * @brief Git submodule management utilities
- *
- * Submodule support in libgit2 builds a list of known submodules and keeps
- * it in the repository. The list is built from the .gitmodules file, the
- * .git/config file, the index, and the HEAD tree. Items in the working
- * directory that look like submodules (i.e. a git repo) but are not
- * mentioned in those places won't be tracked.
- *
- * @defgroup git_submodule Git submodule management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Return codes for submodule status.
- *
- * A combination of these flags will be returned to describe the status of a
- * submodule. Depending on the "ignore" property of the submodule, some of
- * the flags may never be returned because they indicate changes that are
- * supposed to be ignored.
- *
- * Submodule info is contained in 4 places: the HEAD tree, the index, config
- * files (both .git/config and .gitmodules), and the working directory. Any
- * or all of those places might be missing information about the submodule
- * depending on what state the repo is in. We consider all four places to
- * build the combination of status flags.
- *
- * There are four values that are not really status, but give basic info
- * about what sources of submodule data are available. These will be
- * returned even if ignore is set to "ALL".
- *
- * * IN_HEAD - superproject head contains submodule
- * * IN_INDEX - superproject index contains submodule
- * * IN_CONFIG - superproject gitmodules has submodule
- * * IN_WD - superproject workdir has submodule
- *
- * The following values will be returned so long as ignore is not "ALL".
- *
- * * INDEX_ADDED - in index, not in head
- * * INDEX_DELETED - in head, not in index
- * * INDEX_MODIFIED - index and head don't match
- * * WD_UNINITIALIZED - workdir contains empty directory
- * * WD_ADDED - in workdir, not index
- * * WD_DELETED - in index, not workdir
- * * WD_MODIFIED - index and workdir head don't match
- *
- * The following can only be returned if ignore is "NONE" or "UNTRACKED".
- *
- * * WD_INDEX_MODIFIED - submodule workdir index is dirty
- * * WD_WD_MODIFIED - submodule workdir has modified files
- *
- * Lastly, the following will only be returned for ignore "NONE".
- *
- * * WD_UNTRACKED - wd contains untracked files
- */
-typedef enum {
- GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
- GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
- GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
- GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
- GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
- GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
- GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
- GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
- GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
- GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
- GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
- GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
- GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
- GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
-} git_submodule_status_t;
-
-#define GIT_SUBMODULE_STATUS__IN_FLAGS 0x000Fu
-#define GIT_SUBMODULE_STATUS__INDEX_FLAGS 0x0070u
-#define GIT_SUBMODULE_STATUS__WD_FLAGS 0x3F80u
-
-#define GIT_SUBMODULE_STATUS_IS_UNMODIFIED(S) \
- (((S) & ~GIT_SUBMODULE_STATUS__IN_FLAGS) == 0)
-
-#define GIT_SUBMODULE_STATUS_IS_INDEX_UNMODIFIED(S) \
- (((S) & GIT_SUBMODULE_STATUS__INDEX_FLAGS) == 0)
-
-#define GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(S) \
- (((S) & (GIT_SUBMODULE_STATUS__WD_FLAGS & \
- ~GIT_SUBMODULE_STATUS_WD_UNINITIALIZED)) == 0)
-
-#define GIT_SUBMODULE_STATUS_IS_WD_DIRTY(S) \
- (((S) & (GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED | \
- GIT_SUBMODULE_STATUS_WD_WD_MODIFIED | \
- GIT_SUBMODULE_STATUS_WD_UNTRACKED)) != 0)
-
-/**
- * Function pointer to receive each submodule
- *
- * @param sm git_submodule currently being visited
- * @param name name of the submodule
- * @param payload value you passed to the foreach function as payload
- * @return 0 on success or error code
- */
-typedef int (*git_submodule_cb)(
- git_submodule *sm, const char *name, void *payload);
-
-/**
- * Submodule update options structure
- *
- * Use the GIT_SUBMODULE_UPDATE_OPTIONS_INIT to get the default settings,
- * like this:
- *
- * git_submodule_update_options opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
- */
-typedef struct git_submodule_update_options {
- unsigned int version;
-
- /**
- * These options are passed to the checkout step. To disable
- * checkout, set the `checkout_strategy` to
- * `GIT_CHECKOUT_NONE`. Generally you will want the use
- * GIT_CHECKOUT_SAFE to update files in the working
- * directory. Use the `clone_checkout_strategy` field
- * to set the checkout strategy that will be used in
- * the case where update needs to clone the repository.
- */
- git_checkout_options checkout_opts;
-
- /**
- * Options which control the fetch, including callbacks.
- *
- * The callbacks to use for reporting fetch progress, and for acquiring
- * credentials in the event they are needed.
- */
- git_fetch_options fetch_opts;
-
- /**
- * The checkout strategy to use when the sub repository needs to
- * be cloned. Use GIT_CHECKOUT_SAFE to create all files
- * in the working directory for the newly cloned repository.
- */
- unsigned int clone_checkout_strategy;
-
- /**
- * Allow fetching from the submodule's default remote if the target
- * commit isn't found. Enabled by default.
- */
- int allow_fetch;
-} git_submodule_update_options;
-
-#define GIT_SUBMODULE_UPDATE_OPTIONS_VERSION 1
-#define GIT_SUBMODULE_UPDATE_OPTIONS_INIT \
- { GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \
- { GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \
- GIT_FETCH_OPTIONS_INIT, GIT_CHECKOUT_SAFE, 1 }
-
-/**
- * Initializes a `git_submodule_update_options` with default values.
- * Equivalent to creating an instance with GIT_SUBMODULE_UPDATE_OPTIONS_INIT.
- *
- * @param opts The `git_submodule_update_options` instance to initialize.
- * @param version Version of struct; pass `GIT_SUBMODULE_UPDATE_OPTIONS_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_submodule_update_init_options(
- git_submodule_update_options *opts, unsigned int version);
-
-/**
- * Update a submodule. This will clone a missing submodule and
- * checkout the subrepository to the commit specified in the index of
- * the containing repository. If the submodule repository doesn't contain
- * the target commit (e.g. because fetchRecurseSubmodules isn't set), then
- * the submodule is fetched using the fetch options supplied in options.
- *
- * @param submodule Submodule object
- * @param init If the submodule is not initialized, setting this flag to true
- * will initialize the submodule before updating. Otherwise, this will
- * return an error if attempting to update an uninitialzed repository.
- * but setting this to true forces them to be updated.
- * @param options configuration options for the update. If NULL, the
- * function works as though GIT_SUBMODULE_UPDATE_OPTIONS_INIT was passed.
- * @return 0 on success, any non-zero return value from a callback
- * function, or a negative value to indicate an error (use
- * `giterr_last` for a detailed error message).
- */
-GIT_EXTERN(int) git_submodule_update(git_submodule *submodule, int init, git_submodule_update_options *options);
-
-/**
- * Lookup submodule information by name or path.
- *
- * Given either the submodule name or path (they are usually the same), this
- * returns a structure describing the submodule.
- *
- * There are two expected error scenarios:
- *
- * - The submodule is not mentioned in the HEAD, the index, and the config,
- * but does "exist" in the working directory (i.e. there is a subdirectory
- * that appears to be a Git repository). In this case, this function
- * returns GIT_EEXISTS to indicate a sub-repository exists but not in a
- * state where a git_submodule can be instantiated.
- * - The submodule is not mentioned in the HEAD, index, or config and the
- * working directory doesn't contain a value git repo at that path.
- * There may or may not be anything else at that path, but nothing that
- * looks like a submodule. In this case, this returns GIT_ENOTFOUND.
- *
- * You must call `git_submodule_free` when done with the submodule.
- *
- * @param out Output ptr to submodule; pass NULL to just get return code
- * @param repo The parent repository
- * @param name The name of or path to the submodule; trailing slashes okay
- * @return 0 on success, GIT_ENOTFOUND if submodule does not exist,
- * GIT_EEXISTS if a repository is found in working directory only,
- * -1 on other errors.
- */
-GIT_EXTERN(int) git_submodule_lookup(
- git_submodule **out,
- git_repository *repo,
- const char *name);
-
-/**
- * Release a submodule
- *
- * @param submodule Submodule object
- */
-GIT_EXTERN(void) git_submodule_free(git_submodule *submodule);
-
-/**
- * Iterate over all tracked submodules of a repository.
- *
- * See the note on `git_submodule` above. This iterates over the tracked
- * submodules as described therein.
- *
- * If you are concerned about items in the working directory that look like
- * submodules but are not tracked, the diff API will generate a diff record
- * for workdir items that look like submodules but are not tracked, showing
- * them as added in the workdir. Also, the status API will treat the entire
- * subdirectory of a contained git repo as a single GIT_STATUS_WT_NEW item.
- *
- * @param repo The repository
- * @param callback Function to be called with the name of each submodule.
- * Return a non-zero value to terminate the iteration.
- * @param payload Extra data to pass to callback
- * @return 0 on success, -1 on error, or non-zero return value of callback
- */
-GIT_EXTERN(int) git_submodule_foreach(
- git_repository *repo,
- git_submodule_cb callback,
- void *payload);
-
-/**
- * Set up a new git submodule for checkout.
- *
- * This does "git submodule add" up to the fetch and checkout of the
- * submodule contents. It preps a new submodule, creates an entry in
- * .gitmodules and creates an empty initialized repository either at the
- * given path in the working directory or in .git/modules with a gitlink
- * from the working directory to the new repo.
- *
- * To fully emulate "git submodule add" call this function, then open the
- * submodule repo and perform the clone step as needed. Lastly, call
- * `git_submodule_add_finalize()` to wrap up adding the new submodule and
- * .gitmodules to the index to be ready to commit.
- *
- * You must call `git_submodule_free` on the submodule object when done.
- *
- * @param out The newly created submodule ready to open for clone
- * @param repo The repository in which you want to create the submodule
- * @param url URL for the submodule's remote
- * @param path Path at which the submodule should be created
- * @param use_gitlink Should workdir contain a gitlink to the repo in
- * .git/modules vs. repo directly in workdir.
- * @return 0 on success, GIT_EEXISTS if submodule already exists,
- * -1 on other errors.
- */
-GIT_EXTERN(int) git_submodule_add_setup(
- git_submodule **out,
- git_repository *repo,
- const char *url,
- const char *path,
- int use_gitlink);
-
-/**
- * Resolve the setup of a new git submodule.
- *
- * This should be called on a submodule once you have called add setup
- * and done the clone of the submodule. This adds the .gitmodules file
- * and the newly cloned submodule to the index to be ready to be committed
- * (but doesn't actually do the commit).
- *
- * @param submodule The submodule to finish adding.
- */
-GIT_EXTERN(int) git_submodule_add_finalize(git_submodule *submodule);
-
-/**
- * Add current submodule HEAD commit to index of superproject.
- *
- * @param submodule The submodule to add to the index
- * @param write_index Boolean if this should immediately write the index
- * file. If you pass this as false, you will have to get the
- * git_index and explicitly call `git_index_write()` on it to
- * save the change.
- * @return 0 on success, <0 on failure
- */
-GIT_EXTERN(int) git_submodule_add_to_index(
- git_submodule *submodule,
- int write_index);
-
-/**
- * Get the containing repository for a submodule.
- *
- * This returns a pointer to the repository that contains the submodule.
- * This is a just a reference to the repository that was passed to the
- * original `git_submodule_lookup()` call, so if that repository has been
- * freed, then this may be a dangling reference.
- *
- * @param submodule Pointer to submodule object
- * @return Pointer to `git_repository`
- */
-GIT_EXTERN(git_repository *) git_submodule_owner(git_submodule *submodule);
-
-/**
- * Get the name of submodule.
- *
- * @param submodule Pointer to submodule object
- * @return Pointer to the submodule name
- */
-GIT_EXTERN(const char *) git_submodule_name(git_submodule *submodule);
-
-/**
- * Get the path to the submodule.
- *
- * The path is almost always the same as the submodule name, but the
- * two are actually not required to match.
- *
- * @param submodule Pointer to submodule object
- * @return Pointer to the submodule path
- */
-GIT_EXTERN(const char *) git_submodule_path(git_submodule *submodule);
-
-/**
- * Get the URL for the submodule.
- *
- * @param submodule Pointer to submodule object
- * @return Pointer to the submodule url
- */
-GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule);
-
-/**
- * Resolve a submodule url relative to the given repository.
- *
- * @param out buffer to store the absolute submodule url in
- * @param repo Pointer to repository object
- * @param url Relative url
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url);
-
-/**
-* Get the branch for the submodule.
-*
-* @param submodule Pointer to submodule object
-* @return Pointer to the submodule branch
-*/
-GIT_EXTERN(const char *) git_submodule_branch(git_submodule *submodule);
-
-/**
- * Set the branch for the submodule in the configuration
- *
- * After calling this, you may wish to call `git_submodule_sync()` to
- * write the changes to the checked out submodule repository.
- *
- * @param repo the repository to affect
- * @param name the name of the submodule to configure
- * @param branch Branch that should be used for the submodule
- * @return 0 on success, <0 on failure
- */
-GIT_EXTERN(int) git_submodule_set_branch(git_repository *repo, const char *name, const char *branch);
-
-/**
- * Set the URL for the submodule in the configuration
- *
- *
- * After calling this, you may wish to call `git_submodule_sync()` to
- * write the changes to the checked out submodule repository.
- *
- * @param repo the repository to affect
- * @param name the name of the submodule to configure
- * @param url URL that should be used for the submodule
- * @return 0 on success, <0 on failure
- */
-GIT_EXTERN(int) git_submodule_set_url(git_repository *repo, const char *name, const char *url);
-
-/**
- * Get the OID for the submodule in the index.
- *
- * @param submodule Pointer to submodule object
- * @return Pointer to git_oid or NULL if submodule is not in index.
- */
-GIT_EXTERN(const git_oid *) git_submodule_index_id(git_submodule *submodule);
-
-/**
- * Get the OID for the submodule in the current HEAD tree.
- *
- * @param submodule Pointer to submodule object
- * @return Pointer to git_oid or NULL if submodule is not in the HEAD.
- */
-GIT_EXTERN(const git_oid *) git_submodule_head_id(git_submodule *submodule);
-
-/**
- * Get the OID for the submodule in the current working directory.
- *
- * This returns the OID that corresponds to looking up 'HEAD' in the checked
- * out submodule. If there are pending changes in the index or anything
- * else, this won't notice that. You should call `git_submodule_status()`
- * for a more complete picture about the state of the working directory.
- *
- * @param submodule Pointer to submodule object
- * @return Pointer to git_oid or NULL if submodule is not checked out.
- */
-GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule);
-
-/**
- * Get the ignore rule that will be used for the submodule.
- *
- * These values control the behavior of `git_submodule_status()` for this
- * submodule. There are four ignore values:
- *
- * - **GIT_SUBMODULE_IGNORE_NONE** will consider any change to the contents
- * of the submodule from a clean checkout to be dirty, including the
- * addition of untracked files. This is the default if unspecified.
- * - **GIT_SUBMODULE_IGNORE_UNTRACKED** examines the contents of the
- * working tree (i.e. call `git_status_foreach()` on the submodule) but
- * UNTRACKED files will not count as making the submodule dirty.
- * - **GIT_SUBMODULE_IGNORE_DIRTY** means to only check if the HEAD of the
- * submodule has moved for status. This is fast since it does not need to
- * scan the working tree of the submodule at all.
- * - **GIT_SUBMODULE_IGNORE_ALL** means not to open the submodule repo.
- * The working directory will be consider clean so long as there is a
- * checked out version present.
- *
- * @param submodule The submodule to check
- * @return The current git_submodule_ignore_t valyue what will be used for
- * this submodule.
- */
-GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
- git_submodule *submodule);
-
-/**
- * Set the ignore rule for the submodule in the configuration
- *
- * This does not affect any currently-loaded instances.
- *
- * @param repo the repository to affect
- * @param name the name of the submdule
- * @param ignore The new value for the ignore rule
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_submodule_set_ignore(
- git_repository *repo,
- const char *name,
- git_submodule_ignore_t ignore);
-
-/**
- * Get the update rule that will be used for the submodule.
- *
- * This value controls the behavior of the `git submodule update` command.
- * There are four useful values documented with `git_submodule_update_t`.
- *
- * @param submodule The submodule to check
- * @return The current git_submodule_update_t value that will be used
- * for this submodule.
- */
-GIT_EXTERN(git_submodule_update_t) git_submodule_update_strategy(
- git_submodule *submodule);
-
-/**
- * Set the update rule for the submodule in the configuration
- *
- * This setting won't affect any existing instances.
- *
- * @param repo the repository to affect
- * @param name the name of the submodule to configure
- * @param update The new value to use
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_submodule_set_update(
- git_repository *repo,
- const char *name,
- git_submodule_update_t update);
-
-/**
- * Read the fetchRecurseSubmodules rule for a submodule.
- *
- * This accesses the submodule.<name>.fetchRecurseSubmodules value for
- * the submodule that controls fetching behavior for the submodule.
- *
- * Note that at this time, libgit2 does not honor this setting and the
- * fetch functionality current ignores submodules.
- *
- * @return 0 if fetchRecurseSubmodules is false, 1 if true
- */
-GIT_EXTERN(git_submodule_recurse_t) git_submodule_fetch_recurse_submodules(
- git_submodule *submodule);
-
-/**
- * Set the fetchRecurseSubmodules rule for a submodule in the configuration
- *
- * This setting won't affect any existing instances.
- *
- * @param repo the repository to affect
- * @param name the submodule to configure
- * @param fetch_recurse_submodules Boolean value
- * @return old value for fetchRecurseSubmodules
- */
-GIT_EXTERN(int) git_submodule_set_fetch_recurse_submodules(
- git_repository *repo,
- const char *name,
- git_submodule_recurse_t fetch_recurse_submodules);
-
-/**
- * Copy submodule info into ".git/config" file.
- *
- * Just like "git submodule init", this copies information about the
- * submodule into ".git/config". You can use the accessor functions
- * above to alter the in-memory git_submodule object and control what
- * is written to the config, overriding what is in .gitmodules.
- *
- * @param submodule The submodule to write into the superproject config
- * @param overwrite By default, existing entries will not be overwritten,
- * but setting this to true forces them to be updated.
- * @return 0 on success, <0 on failure.
- */
-GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite);
-
-/**
- * Set up the subrepository for a submodule in preparation for clone.
- *
- * This function can be called to init and set up a submodule
- * repository from a submodule in preparation to clone it from
- * its remote.
- *
- * @param out Output pointer to the created git repository.
- * @param sm The submodule to create a new subrepository from.
- * @param use_gitlink Should the workdir contain a gitlink to
- * the repo in .git/modules vs. repo directly in workdir.
- * @return 0 on success, <0 on failure.
- */
-GIT_EXTERN(int) git_submodule_repo_init(
- git_repository **out,
- const git_submodule *sm,
- int use_gitlink);
-
-/**
- * Copy submodule remote info into submodule repo.
- *
- * This copies the information about the submodules URL into the checked out
- * submodule config, acting like "git submodule sync". This is useful if
- * you have altered the URL for the submodule (or it has been altered by a
- * fetch of upstream changes) and you need to update your local repo.
- */
-GIT_EXTERN(int) git_submodule_sync(git_submodule *submodule);
-
-/**
- * Open the repository for a submodule.
- *
- * This is a newly opened repository object. The caller is responsible for
- * calling `git_repository_free()` on it when done. Multiple calls to this
- * function will return distinct `git_repository` objects. This will only
- * work if the submodule is checked out into the working directory.
- *
- * @param repo Pointer to the submodule repo which was opened
- * @param submodule Submodule to be opened
- * @return 0 on success, <0 if submodule repo could not be opened.
- */
-GIT_EXTERN(int) git_submodule_open(
- git_repository **repo,
- git_submodule *submodule);
-
-/**
- * Reread submodule info from config, index, and HEAD.
- *
- * Call this to reread cached submodule information for this submodule if
- * you have reason to believe that it has changed.
- *
- * @param submodule The submodule to reload
- * @param force Force reload even if the data doesn't seem out of date
- * @return 0 on success, <0 on error
- */
-GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule, int force);
-
-/**
- * Get the status for a submodule.
- *
- * This looks at a submodule and tries to determine the status. It
- * will return a combination of the `GIT_SUBMODULE_STATUS` values above.
- * How deeply it examines the working directory to do this will depend
- * on the `git_submodule_ignore_t` value for the submodule.
- *
- * @param status Combination of `GIT_SUBMODULE_STATUS` flags
- * @param repo the repository in which to look
- * @param name name of the submodule
- * @param ignore the ignore rules to follow
- * @return 0 on success, <0 on error
- */
-GIT_EXTERN(int) git_submodule_status(
- unsigned int *status,
- git_repository *repo,
- const char *name,
- git_submodule_ignore_t ignore);
-
-/**
- * Get the locations of submodule information.
- *
- * This is a bit like a very lightweight version of `git_submodule_status`.
- * It just returns a made of the first four submodule status values (i.e.
- * the ones like GIT_SUBMODULE_STATUS_IN_HEAD, etc) that tell you where the
- * submodule data comes from (i.e. the HEAD commit, gitmodules file, etc.).
- * This can be useful if you want to know if the submodule is present in the
- * working directory at this point in time, etc.
- *
- * @param location_status Combination of first four `GIT_SUBMODULE_STATUS` flags
- * @param submodule Submodule for which to get status
- * @return 0 on success, <0 on error
- */
-GIT_EXTERN(int) git_submodule_location(
- unsigned int *location_status,
- git_submodule *submodule);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_commit_h__
-#define INCLUDE_sys_git_commit_h__
-
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/oid.h"
-
-/**
- * @file git2/sys/commit.h
- * @brief Low-level Git commit creation
- * @defgroup git_backend Git custom backend APIs
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Create new commit in the repository from a list of `git_oid` values.
- *
- * See documentation for `git_commit_create()` for information about the
- * parameters, as the meaning is identical excepting that `tree` and
- * `parents` now take `git_oid`. This is a dangerous API in that nor
- * the `tree`, neither the `parents` list of `git_oid`s are checked for
- * validity.
- *
- * @see git_commit_create
- */
-GIT_EXTERN(int) git_commit_create_from_ids(
- git_oid *id,
- git_repository *repo,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_oid *tree,
- size_t parent_count,
- const git_oid *parents[]);
-
-/**
- * Callback function to return parents for commit.
- *
- * This is invoked with the count of the number of parents processed so far
- * along with the user supplied payload. This should return a git_oid of
- * the next parent or NULL if all parents have been provided.
- */
-typedef const git_oid *(*git_commit_parent_callback)(size_t idx, void *payload);
-
-/**
- * Create a new commit in the repository with an callback to supply parents.
- *
- * See documentation for `git_commit_create()` for information about the
- * parameters, as the meaning is identical excepting that `tree` takes a
- * `git_oid` and doesn't check for validity, and `parent_cb` is invoked
- * with `parent_payload` and should return `git_oid` values or NULL to
- * indicate that all parents are accounted for.
- *
- * @see git_commit_create
- */
-GIT_EXTERN(int) git_commit_create_from_callback(
- git_oid *id,
- git_repository *repo,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_oid *tree,
- git_commit_parent_callback parent_cb,
- void *parent_payload);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_config_backend_h__
-#define INCLUDE_sys_git_config_backend_h__
-
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/config.h"
-
-/**
- * @file git2/sys/config.h
- * @brief Git config backend routines
- * @defgroup git_backend Git custom backend APIs
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Every iterator must have this struct as its first element, so the
- * API can talk to it. You'd define your iterator as
- *
- * struct my_iterator {
- * git_config_iterator parent;
- * ...
- * }
- *
- * and assign `iter->parent.backend` to your `git_config_backend`.
- */
-struct git_config_iterator {
- git_config_backend *backend;
- unsigned int flags;
-
- /**
- * Return the current entry and advance the iterator. The
- * memory belongs to the library.
- */
- int (*next)(git_config_entry **entry, git_config_iterator *iter);
-
- /**
- * Free the iterator
- */
- void (*free)(git_config_iterator *iter);
-};
-
-/**
- * Generic backend that implements the interface to
- * access a configuration file
- */
-struct git_config_backend {
- unsigned int version;
- /** True if this backend is for a snapshot */
- int readonly;
- struct git_config *cfg;
-
- /* Open means open the file/database and parse if necessary */
- int (*open)(struct git_config_backend *, git_config_level_t level);
- int (*get)(struct git_config_backend *, const char *key, git_config_entry **entry);
- int (*set)(struct git_config_backend *, const char *key, const char *value);
- int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
- int (*del)(struct git_config_backend *, const char *key);
- int (*del_multivar)(struct git_config_backend *, const char *key, const char *regexp);
- int (*iterator)(git_config_iterator **, struct git_config_backend *);
- /** Produce a read-only version of this backend */
- int (*snapshot)(struct git_config_backend **, struct git_config_backend *);
- /**
- * Lock this backend.
- *
- * Prevent any writes to the data store backing this
- * backend. Any updates must not be visible to any other
- * readers.
- */
- int (*lock)(struct git_config_backend *);
- /**
- * Unlock the data store backing this backend. If success is
- * true, the changes should be committed, otherwise rolled
- * back.
- */
- int (*unlock)(struct git_config_backend *, int success);
- void (*free)(struct git_config_backend *);
-};
-#define GIT_CONFIG_BACKEND_VERSION 1
-#define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION}
-
-/**
- * Initializes a `git_config_backend` with default values. Equivalent to
- * creating an instance with GIT_CONFIG_BACKEND_INIT.
- *
- * @param backend the `git_config_backend` struct to initialize.
- * @param version Version of struct; pass `GIT_CONFIG_BACKEND_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_config_init_backend(
- git_config_backend *backend,
- unsigned int version);
-
-/**
- * Add a generic config file instance to an existing config
- *
- * Note that the configuration object will free the file
- * automatically.
- *
- * Further queries on this config object will access each
- * of the config file instances in order (instances with
- * a higher priority level will be accessed first).
- *
- * @param cfg the configuration to add the file to
- * @param file the configuration file (backend) to add
- * @param level the priority level of the backend
- * @param force if a config file already exists for the given
- * priority level, replace it
- * @return 0 on success, GIT_EEXISTS when adding more than one file
- * for a given priority level (and force_replace set to 0), or error code
- */
-GIT_EXTERN(int) git_config_add_backend(
- git_config *cfg,
- git_config_backend *file,
- git_config_level_t level,
- int force);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_diff_h__
-#define INCLUDE_sys_git_diff_h__
-
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/oid.h"
-#include "git2/diff.h"
-#include "git2/status.h"
-
-/**
- * @file git2/sys/diff.h
- * @brief Low-level Git diff utilities
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Diff print callback that writes to a git_buf.
- *
- * This function is provided not for you to call it directly, but instead
- * so you can use it as a function pointer to the `git_diff_print` or
- * `git_patch_print` APIs. When using those APIs, you specify a callback
- * to actually handle the diff and/or patch data.
- *
- * Use this callback to easily write that data to a `git_buf` buffer. You
- * must pass a `git_buf *` value as the payload to the `git_diff_print`
- * and/or `git_patch_print` function. The data will be appended to the
- * buffer (after any existing content).
- */
-GIT_EXTERN(int) git_diff_print_callback__to_buf(
- const git_diff_delta *delta,
- const git_diff_hunk *hunk,
- const git_diff_line *line,
- void *payload); /**< payload must be a `git_buf *` */
-
-/**
- * Diff print callback that writes to stdio FILE handle.
- *
- * This function is provided not for you to call it directly, but instead
- * so you can use it as a function pointer to the `git_diff_print` or
- * `git_patch_print` APIs. When using those APIs, you specify a callback
- * to actually handle the diff and/or patch data.
- *
- * Use this callback to easily write that data to a stdio FILE handle. You
- * must pass a `FILE *` value (such as `stdout` or `stderr` or the return
- * value from `fopen()`) as the payload to the `git_diff_print`
- * and/or `git_patch_print` function. If you pass NULL, this will write
- * data to `stdout`.
- */
-GIT_EXTERN(int) git_diff_print_callback__to_file_handle(
- const git_diff_delta *delta,
- const git_diff_hunk *hunk,
- const git_diff_line *line,
- void *payload); /**< payload must be a `FILE *` */
-
-
-/**
- * Performance data from diffing
- */
-typedef struct {
- unsigned int version;
- size_t stat_calls; /**< Number of stat() calls performed */
- size_t oid_calculations; /**< Number of ID calculations */
-} git_diff_perfdata;
-
-#define GIT_DIFF_PERFDATA_VERSION 1
-#define GIT_DIFF_PERFDATA_INIT {GIT_DIFF_PERFDATA_VERSION,0,0}
-
-/**
- * Get performance data for a diff object.
- *
- * @param out Structure to be filled with diff performance data
- * @param diff Diff to read performance data from
- * @return 0 for success, <0 for error
- */
-GIT_EXTERN(int) git_diff_get_perfdata(
- git_diff_perfdata *out, const git_diff *diff);
-
-/**
- * Get performance data for diffs from a git_status_list
- */
-GIT_EXTERN(int) git_status_list_get_perfdata(
- git_diff_perfdata *out, const git_status_list *status);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_filter_h__
-#define INCLUDE_sys_git_filter_h__
-
-#include "git2/filter.h"
-
-/**
- * @file git2/sys/filter.h
- * @brief Git filter backend and plugin routines
- * @defgroup git_backend Git custom backend APIs
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Look up a filter by name
- *
- * @param name The name of the filter
- * @return Pointer to the filter object or NULL if not found
- */
-GIT_EXTERN(git_filter *) git_filter_lookup(const char *name);
-
-#define GIT_FILTER_CRLF "crlf"
-#define GIT_FILTER_IDENT "ident"
-
-/**
- * This is priority that the internal CRLF filter will be registered with
- */
-#define GIT_FILTER_CRLF_PRIORITY 0
-
-/**
- * This is priority that the internal ident filter will be registered with
- */
-#define GIT_FILTER_IDENT_PRIORITY 100
-
-/**
- * This is priority to use with a custom filter to imitate a core Git
- * filter driver, so that it will be run last on checkout and first on
- * checkin. You do not have to use this, but it helps compatibility.
- */
-#define GIT_FILTER_DRIVER_PRIORITY 200
-
-/**
- * Create a new empty filter list
- *
- * Normally you won't use this because `git_filter_list_load` will create
- * the filter list for you, but you can use this in combination with the
- * `git_filter_lookup` and `git_filter_list_push` functions to assemble
- * your own chains of filters.
- */
-GIT_EXTERN(int) git_filter_list_new(
- git_filter_list **out,
- git_repository *repo,
- git_filter_mode_t mode,
- uint32_t options);
-
-/**
- * Add a filter to a filter list with the given payload.
- *
- * Normally you won't have to do this because the filter list is created
- * by calling the "check" function on registered filters when the filter
- * attributes are set, but this does allow more direct manipulation of
- * filter lists when desired.
- *
- * Note that normally the "check" function can set up a payload for the
- * filter. Using this function, you can either pass in a payload if you
- * know the expected payload format, or you can pass NULL. Some filters
- * may fail with a NULL payload. Good luck!
- */
-GIT_EXTERN(int) git_filter_list_push(
- git_filter_list *fl, git_filter *filter, void *payload);
-
-/**
- * Look up how many filters are in the list
- *
- * We will attempt to apply all of these filters to any data passed in,
- * but note that the filter apply action still has the option of skipping
- * data that is passed in (for example, the CRLF filter will skip data
- * that appears to be binary).
- *
- * @param fl A filter list
- * @return The number of filters in the list
- */
-GIT_EXTERN(size_t) git_filter_list_length(const git_filter_list *fl);
-
-/**
- * A filter source represents a file/blob to be processed
- */
-typedef struct git_filter_source git_filter_source;
-
-/**
- * Get the repository that the source data is coming from.
- */
-GIT_EXTERN(git_repository *) git_filter_source_repo(const git_filter_source *src);
-
-/**
- * Get the path that the source data is coming from.
- */
-GIT_EXTERN(const char *) git_filter_source_path(const git_filter_source *src);
-
-/**
- * Get the file mode of the source file
- * If the mode is unknown, this will return 0
- */
-GIT_EXTERN(uint16_t) git_filter_source_filemode(const git_filter_source *src);
-
-/**
- * Get the OID of the source
- * If the OID is unknown (often the case with GIT_FILTER_CLEAN) then
- * this will return NULL.
- */
-GIT_EXTERN(const git_oid *) git_filter_source_id(const git_filter_source *src);
-
-/**
- * Get the git_filter_mode_t to be used
- */
-GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *src);
-
-/**
- * Get the combination git_filter_flag_t options to be applied
- */
-GIT_EXTERN(uint32_t) git_filter_source_flags(const git_filter_source *src);
-
-/**
- * Initialize callback on filter
- *
- * Specified as `filter.initialize`, this is an optional callback invoked
- * before a filter is first used. It will be called once at most.
- *
- * If non-NULL, the filter's `initialize` callback will be invoked right
- * before the first use of the filter, so you can defer expensive
- * initialization operations (in case libgit2 is being used in a way that
- * doesn't need the filter).
- */
-typedef int (*git_filter_init_fn)(git_filter *self);
-
-/**
- * Shutdown callback on filter
- *
- * Specified as `filter.shutdown`, this is an optional callback invoked
- * when the filter is unregistered or when libgit2 is shutting down. It
- * will be called once at most and should release resources as needed.
- * This may be called even if the `initialize` callback was not made.
- *
- * Typically this function will free the `git_filter` object itself.
- */
-typedef void (*git_filter_shutdown_fn)(git_filter *self);
-
-/**
- * Callback to decide if a given source needs this filter
- *
- * Specified as `filter.check`, this is an optional callback that checks
- * if filtering is needed for a given source.
- *
- * It should return 0 if the filter should be applied (i.e. success),
- * GIT_PASSTHROUGH if the filter should not be applied, or an error code
- * to fail out of the filter processing pipeline and return to the caller.
- *
- * The `attr_values` will be set to the values of any attributes given in
- * the filter definition. See `git_filter` below for more detail.
- *
- * The `payload` will be a pointer to a reference payload for the filter.
- * This will start as NULL, but `check` can assign to this pointer for
- * later use by the `apply` callback. Note that the value should be heap
- * allocated (not stack), so that it doesn't go away before the `apply`
- * callback can use it. If a filter allocates and assigns a value to the
- * `payload`, it will need a `cleanup` callback to free the payload.
- */
-typedef int (*git_filter_check_fn)(
- git_filter *self,
- void **payload, /* points to NULL ptr on entry, may be set */
- const git_filter_source *src,
- const char **attr_values);
-
-/**
- * Callback to actually perform the data filtering
- *
- * Specified as `filter.apply`, this is the callback that actually filters
- * data. If it successfully writes the output, it should return 0. Like
- * `check`, it can return GIT_PASSTHROUGH to indicate that the filter
- * doesn't want to run. Other error codes will stop filter processing and
- * return to the caller.
- *
- * The `payload` value will refer to any payload that was set by the
- * `check` callback. It may be read from or written to as needed.
- */
-typedef int (*git_filter_apply_fn)(
- git_filter *self,
- void **payload, /* may be read and/or set */
- git_buf *to,
- const git_buf *from,
- const git_filter_source *src);
-
-typedef int (*git_filter_stream_fn)(
- git_writestream **out,
- git_filter *self,
- void **payload,
- const git_filter_source *src,
- git_writestream *next);
-
-/**
- * Callback to clean up after filtering has been applied
- *
- * Specified as `filter.cleanup`, this is an optional callback invoked
- * after the filter has been applied. If the `check` or `apply` callbacks
- * allocated a `payload` to keep per-source filter state, use this
- * callback to free that payload and release resources as required.
- */
-typedef void (*git_filter_cleanup_fn)(
- git_filter *self,
- void *payload);
-
-/**
- * Filter structure used to register custom filters.
- *
- * To associate extra data with a filter, allocate extra data and put the
- * `git_filter` struct at the start of your data buffer, then cast the
- * `self` pointer to your larger structure when your callback is invoked.
- */
-struct git_filter {
- /** The `version` field should be set to `GIT_FILTER_VERSION`. */
- unsigned int version;
-
- /**
- * A whitespace-separated list of attribute names to check for this
- * filter (e.g. "eol crlf text"). If the attribute name is bare, it
- * will be simply loaded and passed to the `check` callback. If it
- * has a value (i.e. "name=value"), the attribute must match that
- * value for the filter to be applied. The value may be a wildcard
- * (eg, "name=*"), in which case the filter will be invoked for any
- * value for the given attribute name. See the attribute parameter
- * of the `check` callback for the attribute value that was specified.
- */
- const char *attributes;
-
- /** Called when the filter is first used for any file. */
- git_filter_init_fn initialize;
-
- /** Called when the filter is removed or unregistered from the system. */
- git_filter_shutdown_fn shutdown;
-
- /**
- * Called to determine whether the filter should be invoked for a
- * given file. If this function returns `GIT_PASSTHROUGH` then the
- * `apply` function will not be invoked and the contents will be passed
- * through unmodified.
- */
- git_filter_check_fn check;
-
- /**
- * Called to actually apply the filter to file contents. If this
- * function returns `GIT_PASSTHROUGH` then the contents will be passed
- * through unmodified.
- */
- git_filter_apply_fn apply;
-
- /**
- * Called to apply the filter in a streaming manner. If this is not
- * specified then the system will call `apply` with the whole buffer.
- */
- git_filter_stream_fn stream;
-
- /** Called when the system is done filtering for a file. */
- git_filter_cleanup_fn cleanup;
-};
-
-#define GIT_FILTER_VERSION 1
-
-/**
- * Register a filter under a given name with a given priority.
- *
- * As mentioned elsewhere, the initialize callback will not be invoked
- * immediately. It is deferred until the filter is used in some way.
- *
- * A filter's attribute checks and `check` and `apply` callbacks will be
- * issued in order of `priority` on smudge (to workdir), and in reverse
- * order of `priority` on clean (to odb).
- *
- * Two filters are preregistered with libgit2:
- * - GIT_FILTER_CRLF with priority 0
- * - GIT_FILTER_IDENT with priority 100
- *
- * Currently the filter registry is not thread safe, so any registering or
- * deregistering of filters must be done outside of any possible usage of
- * the filters (i.e. during application setup or shutdown).
- *
- * @param name A name by which the filter can be referenced. Attempting
- * to register with an in-use name will return GIT_EEXISTS.
- * @param filter The filter definition. This pointer will be stored as is
- * by libgit2 so it must be a durable allocation (either static
- * or on the heap).
- * @param priority The priority for filter application
- * @return 0 on successful registry, error code <0 on failure
- */
-GIT_EXTERN(int) git_filter_register(
- const char *name, git_filter *filter, int priority);
-
-/**
- * Remove the filter with the given name
- *
- * Attempting to remove the builtin libgit2 filters is not permitted and
- * will return an error.
- *
- * Currently the filter registry is not thread safe, so any registering or
- * deregistering of filters must be done outside of any possible usage of
- * the filters (i.e. during application setup or shutdown).
- *
- * @param name The name under which the filter was registered
- * @return 0 on success, error code <0 on failure
- */
-GIT_EXTERN(int) git_filter_unregister(const char *name);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_hashsig_h__
-#define INCLUDE_sys_hashsig_h__
-
-#include "git2/common.h"
-
-GIT_BEGIN_DECL
-
-/**
- * Similarity signature of arbitrary text content based on line hashes
- */
-typedef struct git_hashsig git_hashsig;
-
-/**
- * Options for hashsig computation
- *
- * The options GIT_HASHSIG_NORMAL, GIT_HASHSIG_IGNORE_WHITESPACE,
- * GIT_HASHSIG_SMART_WHITESPACE are exclusive and should not be combined.
- */
-typedef enum {
- /**
- * Use all data
- */
- GIT_HASHSIG_NORMAL = 0,
-
- /**
- * Ignore whitespace
- */
- GIT_HASHSIG_IGNORE_WHITESPACE = (1 << 0),
-
- /**
- * Ignore \r and all space after \n
- */
- GIT_HASHSIG_SMART_WHITESPACE = (1 << 1),
-
- /**
- * Allow hashing of small files
- */
- GIT_HASHSIG_ALLOW_SMALL_FILES = (1 << 2)
-} git_hashsig_option_t;
-
-/**
- * Compute a similarity signature for a text buffer
- *
- * If you have passed the option GIT_HASHSIG_IGNORE_WHITESPACE, then the
- * whitespace will be removed from the buffer while it is being processed,
- * modifying the buffer in place. Sorry about that!
- *
- * @param out The computed similarity signature.
- * @param buf The input buffer.
- * @param buflen The input buffer size.
- * @param opts The signature computation options (see above).
- * @return 0 on success, GIT_EBUFS if the buffer doesn't contain enough data to
- * compute a valid signature (unless GIT_HASHSIG_ALLOW_SMALL_FILES is set), or
- * error code.
- */
-GIT_EXTERN(int) git_hashsig_create(
- git_hashsig **out,
- const char *buf,
- size_t buflen,
- git_hashsig_option_t opts);
-
-/**
- * Compute a similarity signature for a text file
- *
- * This walks through the file, only loading a maximum of 4K of file data at
- * a time. Otherwise, it acts just like `git_hashsig_create`.
- *
- * @param out The computed similarity signature.
- * @param path The path to the input file.
- * @param opts The signature computation options (see above).
- * @return 0 on success, GIT_EBUFS if the buffer doesn't contain enough data to
- * compute a valid signature (unless GIT_HASHSIG_ALLOW_SMALL_FILES is set), or
- * error code.
- */
-GIT_EXTERN(int) git_hashsig_create_fromfile(
- git_hashsig **out,
- const char *path,
- git_hashsig_option_t opts);
-
-/**
- * Release memory for a content similarity signature
- *
- * @param sig The similarity signature to free.
- */
-GIT_EXTERN(void) git_hashsig_free(git_hashsig *sig);
-
-/**
- * Measure similarity score between two similarity signatures
- *
- * @param a The first similarity signature to compare.
- * @param b The second similarity signature to compare.
- * @return [0 to 100] on success as the similarity score, or error code.
- */
-GIT_EXTERN(int) git_hashsig_compare(
- const git_hashsig *a,
- const git_hashsig *b);
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_index_h__
-#define INCLUDE_sys_git_index_h__
-
-/**
- * @file git2/sys/index.h
- * @brief Low-level Git index manipulation routines
- * @defgroup git_backend Git custom backend APIs
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/** Representation of a rename conflict entry in the index. */
-typedef struct git_index_name_entry {
- char *ancestor;
- char *ours;
- char *theirs;
-} git_index_name_entry;
-
-/** Representation of a resolve undo entry in the index. */
-typedef struct git_index_reuc_entry {
- uint32_t mode[3];
- git_oid oid[3];
- char *path;
-} git_index_reuc_entry;
-
-/** @name Conflict Name entry functions
- *
- * These functions work on rename conflict entries.
- */
-/**@{*/
-
-/**
- * Get the count of filename conflict entries currently in the index.
- *
- * @param index an existing index object
- * @return integer of count of current filename conflict entries
- */
-GIT_EXTERN(size_t) git_index_name_entrycount(git_index *index);
-
-/**
- * Get a filename conflict entry from the index.
- *
- * The returned entry is read-only and should not be modified
- * or freed by the caller.
- *
- * @param index an existing index object
- * @param n the position of the entry
- * @return a pointer to the filename conflict entry; NULL if out of bounds
- */
-GIT_EXTERN(const git_index_name_entry *) git_index_name_get_byindex(
- git_index *index, size_t n);
-
-/**
- * Record the filenames involved in a rename conflict.
- *
- * @param index an existing index object
- * @param ancestor the path of the file as it existed in the ancestor
- * @param ours the path of the file as it existed in our tree
- * @param theirs the path of the file as it existed in their tree
- */
-GIT_EXTERN(int) git_index_name_add(git_index *index,
- const char *ancestor, const char *ours, const char *theirs);
-
-/**
- * Remove all filename conflict entries.
- *
- * @param index an existing index object
- */
-GIT_EXTERN(void) git_index_name_clear(git_index *index);
-
-/**@}*/
-
-/** @name Resolve Undo (REUC) index entry manipulation.
- *
- * These functions work on the Resolve Undo index extension and contains
- * data about the original files that led to a merge conflict.
- */
-/**@{*/
-
-/**
- * Get the count of resolve undo entries currently in the index.
- *
- * @param index an existing index object
- * @return integer of count of current resolve undo entries
- */
-GIT_EXTERN(size_t) git_index_reuc_entrycount(git_index *index);
-
-/**
- * Finds the resolve undo entry that points to the given path in the Git
- * index.
- *
- * @param at_pos the address to which the position of the reuc entry is written (optional)
- * @param index an existing index object
- * @param path path to search
- * @return 0 if found, < 0 otherwise (GIT_ENOTFOUND)
- */
-GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path);
-
-/**
- * Get a resolve undo entry from the index.
- *
- * The returned entry is read-only and should not be modified
- * or freed by the caller.
- *
- * @param index an existing index object
- * @param path path to search
- * @return the resolve undo entry; NULL if not found
- */
-GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path);
-
-/**
- * Get a resolve undo entry from the index.
- *
- * The returned entry is read-only and should not be modified
- * or freed by the caller.
- *
- * @param index an existing index object
- * @param n the position of the entry
- * @return a pointer to the resolve undo entry; NULL if out of bounds
- */
-GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n);
-
-/**
- * Adds a resolve undo entry for a file based on the given parameters.
- *
- * The resolve undo entry contains the OIDs of files that were involved
- * in a merge conflict after the conflict has been resolved. This allows
- * conflicts to be re-resolved later.
- *
- * If there exists a resolve undo entry for the given path in the index,
- * it will be removed.
- *
- * This method will fail in bare index instances.
- *
- * @param index an existing index object
- * @param path filename to add
- * @param ancestor_mode mode of the ancestor file
- * @param ancestor_id oid of the ancestor file
- * @param our_mode mode of our file
- * @param our_id oid of our file
- * @param their_mode mode of their file
- * @param their_id oid of their file
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
- int ancestor_mode, const git_oid *ancestor_id,
- int our_mode, const git_oid *our_id,
- int their_mode, const git_oid *their_id);
-
-/**
- * Remove an resolve undo entry from the index
- *
- * @param index an existing index object
- * @param n position of the resolve undo entry to remove
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n);
-
-/**
- * Remove all resolve undo entries from the index
- *
- * @param index an existing index object
- */
-GIT_EXTERN(void) git_index_reuc_clear(git_index *index);
-
-/**@}*/
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_odb_mempack_h__
-#define INCLUDE_sys_git_odb_mempack_h__
-
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/oid.h"
-#include "git2/odb.h"
-
-/**
- * @file git2/sys/mempack.h
- * @brief Custom ODB backend that permits packing objects in-memory
- * @defgroup git_backend Git custom backend APIs
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Instantiate a new mempack backend.
- *
- * The backend must be added to an existing ODB with the highest
- * priority.
- *
- * git_mempack_new(&mempacker);
- * git_repository_odb(&odb, repository);
- * git_odb_add_backend(odb, mempacker, 999);
- *
- * Once the backend has been loaded, all writes to the ODB will
- * instead be queued in memory, and can be finalized with
- * `git_mempack_dump`.
- *
- * Subsequent reads will also be served from the in-memory store
- * to ensure consistency, until the memory store is dumped.
- *
- * @param out Poiter where to store the ODB backend
- * @return 0 on success; error code otherwise
- */
-int git_mempack_new(git_odb_backend **out);
-
-/**
- * Dump all the queued in-memory writes to a packfile.
- *
- * The contents of the packfile will be stored in the given buffer.
- * It is the caller's responsibility to ensure that the generated
- * packfile is available to the repository (e.g. by writing it
- * to disk, or doing something crazy like distributing it across
- * several copies of the repository over a network).
- *
- * Once the generated packfile is available to the repository,
- * call `git_mempack_reset` to cleanup the memory store.
- *
- * Calling `git_mempack_reset` before the packfile has been
- * written to disk will result in an inconsistent repository
- * (the objects in the memory store won't be accessible).
- *
- * @param pack Buffer where to store the raw packfile
- * @param repo The active repository where the backend is loaded
- * @param backend The mempack backend
- * @return 0 on success; error code otherwise
- */
-int git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *backend);
-
-/**
- * Reset the memory packer by clearing all the queued objects.
- *
- * This assumes that `git_mempack_dump` has been called before to
- * store all the queued objects into a single packfile.
- *
- * Alternatively, call `reset` without a previous dump to "undo"
- * all the recently written objects, giving transaction-like
- * semantics to the Git repository.
- *
- * @param backend The mempack backend
- */
-void git_mempack_reset(git_odb_backend *backend);
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_merge_h__
-#define INCLUDE_sys_git_merge_h__
-
-/**
- * @file git2/sys/merge.h
- * @brief Git merge driver backend and plugin routines
- * @defgroup git_backend Git custom backend APIs
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-typedef struct git_merge_driver git_merge_driver;
-
-/**
- * Look up a merge driver by name
- *
- * @param name The name of the merge driver
- * @return Pointer to the merge driver object or NULL if not found
- */
-GIT_EXTERN(git_merge_driver *) git_merge_driver_lookup(const char *name);
-
-#define GIT_MERGE_DRIVER_TEXT "text"
-#define GIT_MERGE_DRIVER_BINARY "binary"
-#define GIT_MERGE_DRIVER_UNION "union"
-
-/**
- * A merge driver source represents the file to be merged
- */
-typedef struct git_merge_driver_source git_merge_driver_source;
-
-/** Get the repository that the source data is coming from. */
-GIT_EXTERN(git_repository *) git_merge_driver_source_repo(
- const git_merge_driver_source *src);
-
-/** Gets the ancestor of the file to merge. */
-GIT_EXTERN(git_index_entry *) git_merge_driver_source_ancestor(
- const git_merge_driver_source *src);
-
-/** Gets the ours side of the file to merge. */
-GIT_EXTERN(git_index_entry *) git_merge_driver_source_ours(
- const git_merge_driver_source *src);
-
-/** Gets the theirs side of the file to merge. */
-GIT_EXTERN(git_index_entry *) git_merge_driver_source_theirs(
- const git_merge_driver_source *src);
-
-/** Gets the merge file options that the merge was invoked with */
-GIT_EXTERN(git_merge_file_options *) git_merge_driver_source_file_options(
- const git_merge_driver_source *src);
-
-
-/**
- * Initialize callback on merge driver
- *
- * Specified as `driver.initialize`, this is an optional callback invoked
- * before a merge driver is first used. It will be called once at most
- * per library lifetime.
- *
- * If non-NULL, the merge driver's `initialize` callback will be invoked
- * right before the first use of the driver, so you can defer expensive
- * initialization operations (in case libgit2 is being used in a way that
- * doesn't need the merge driver).
- */
-typedef int (*git_merge_driver_init_fn)(git_merge_driver *self);
-
-/**
- * Shutdown callback on merge driver
- *
- * Specified as `driver.shutdown`, this is an optional callback invoked
- * when the merge driver is unregistered or when libgit2 is shutting down.
- * It will be called once at most and should release resources as needed.
- * This may be called even if the `initialize` callback was not made.
- *
- * Typically this function will free the `git_merge_driver` object itself.
- */
-typedef void (*git_merge_driver_shutdown_fn)(git_merge_driver *self);
-
-/**
- * Callback to perform the merge.
- *
- * Specified as `driver.apply`, this is the callback that actually does the
- * merge. If it can successfully perform a merge, it should populate
- * `path_out` with a pointer to the filename to accept, `mode_out` with
- * the resultant mode, and `merged_out` with the buffer of the merged file
- * and then return 0. If the driver returns `GIT_PASSTHROUGH`, then the
- * default merge driver should instead be run. It can also return
- * `GIT_EMERGECONFLICT` if the driver is not able to produce a merge result,
- * and the file will remain conflicted. Any other errors will fail and
- * return to the caller.
- *
- * The `filter_name` contains the name of the filter that was invoked, as
- * specified by the file's attributes.
- *
- * The `src` contains the data about the file to be merged.
- */
-typedef int (*git_merge_driver_apply_fn)(
- git_merge_driver *self,
- const char **path_out,
- uint32_t *mode_out,
- git_buf *merged_out,
- const char *filter_name,
- const git_merge_driver_source *src);
-
-/**
- * Merge driver structure used to register custom merge drivers.
- *
- * To associate extra data with a driver, allocate extra data and put the
- * `git_merge_driver` struct at the start of your data buffer, then cast
- * the `self` pointer to your larger structure when your callback is invoked.
- */
-struct git_merge_driver {
- /** The `version` should be set to `GIT_MERGE_DRIVER_VERSION`. */
- unsigned int version;
-
- /** Called when the merge driver is first used for any file. */
- git_merge_driver_init_fn initialize;
-
- /** Called when the merge driver is unregistered from the system. */
- git_merge_driver_shutdown_fn shutdown;
-
- /**
- * Called to merge the contents of a conflict. If this function
- * returns `GIT_PASSTHROUGH` then the default (`text`) merge driver
- * will instead be invoked. If this function returns
- * `GIT_EMERGECONFLICT` then the file will remain conflicted.
- */
- git_merge_driver_apply_fn apply;
-};
-
-#define GIT_MERGE_DRIVER_VERSION 1
-
-/**
- * Register a merge driver under a given name.
- *
- * As mentioned elsewhere, the initialize callback will not be invoked
- * immediately. It is deferred until the driver is used in some way.
- *
- * Currently the merge driver registry is not thread safe, so any
- * registering or deregistering of merge drivers must be done outside of
- * any possible usage of the drivers (i.e. during application setup or
- * shutdown).
- *
- * @param name The name of this driver to match an attribute. Attempting
- * to register with an in-use name will return GIT_EEXISTS.
- * @param driver The merge driver definition. This pointer will be stored
- * as is by libgit2 so it must be a durable allocation (either
- * static or on the heap).
- * @return 0 on successful registry, error code <0 on failure
- */
-GIT_EXTERN(int) git_merge_driver_register(
- const char *name, git_merge_driver *driver);
-
-/**
- * Remove the merge driver with the given name.
- *
- * Attempting to remove the builtin libgit2 merge drivers is not permitted
- * and will return an error.
- *
- * Currently the merge driver registry is not thread safe, so any
- * registering or deregistering of drivers must be done outside of any
- * possible usage of the drivers (i.e. during application setup or shutdown).
- *
- * @param name The name under which the merge driver was registered
- * @return 0 on success, error code <0 on failure
- */
-GIT_EXTERN(int) git_merge_driver_unregister(const char *name);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_odb_backend_h__
-#define INCLUDE_sys_git_odb_backend_h__
-
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/oid.h"
-#include "git2/odb.h"
-
-/**
- * @file git2/sys/backend.h
- * @brief Git custom backend implementors functions
- * @defgroup git_backend Git custom backend APIs
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * An instance for a custom backend
- */
-struct git_odb_backend {
- unsigned int version;
- git_odb *odb;
-
- /* read and read_prefix each return to libgit2 a buffer which
- * will be freed later. The buffer should be allocated using
- * the function git_odb_backend_malloc to ensure that it can
- * be safely freed later. */
- int (* read)(
- void **, size_t *, git_otype *, git_odb_backend *, const git_oid *);
-
- /* To find a unique object given a prefix of its oid. The oid given
- * must be so that the remaining (GIT_OID_HEXSZ - len)*4 bits are 0s.
- */
- int (* read_prefix)(
- git_oid *, void **, size_t *, git_otype *,
- git_odb_backend *, const git_oid *, size_t);
-
- int (* read_header)(
- size_t *, git_otype *, git_odb_backend *, const git_oid *);
-
- /**
- * Write an object into the backend. The id of the object has
- * already been calculated and is passed in.
- */
- int (* write)(
- git_odb_backend *, const git_oid *, const void *, size_t, git_otype);
-
- int (* writestream)(
- git_odb_stream **, git_odb_backend *, git_off_t, git_otype);
-
- int (* readstream)(
- git_odb_stream **, git_odb_backend *, const git_oid *);
-
- int (* exists)(
- git_odb_backend *, const git_oid *);
-
- int (* exists_prefix)(
- git_oid *, git_odb_backend *, const git_oid *, size_t);
-
- /**
- * If the backend implements a refreshing mechanism, it should be exposed
- * through this endpoint. Each call to `git_odb_refresh()` will invoke it.
- *
- * However, the backend implementation should try to stay up-to-date as much
- * as possible by itself as libgit2 will not automatically invoke
- * `git_odb_refresh()`. For instance, a potential strategy for the backend
- * implementation to achieve this could be to internally invoke this
- * endpoint on failed lookups (ie. `exists()`, `read()`, `read_header()`).
- */
- int (* refresh)(git_odb_backend *);
-
- int (* foreach)(
- git_odb_backend *, git_odb_foreach_cb cb, void *payload);
-
- int (* writepack)(
- git_odb_writepack **, git_odb_backend *, git_odb *odb,
- git_transfer_progress_cb progress_cb, void *progress_payload);
-
- /**
- * "Freshens" an already existing object, updating its last-used
- * time. This occurs when `git_odb_write` was called, but the
- * object already existed (and will not be re-written). The
- * underlying implementation may want to update last-used timestamps.
- *
- * If callers implement this, they should return `0` if the object
- * exists and was freshened, and non-zero otherwise.
- */
- int (* freshen)(git_odb_backend *, const git_oid *);
-
- /**
- * Frees any resources held by the odb (including the `git_odb_backend`
- * itself). An odb backend implementation must provide this function.
- */
- void (* free)(git_odb_backend *);
-};
-
-#define GIT_ODB_BACKEND_VERSION 1
-#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
-
-/**
- * Initializes a `git_odb_backend` with default values. Equivalent to
- * creating an instance with GIT_ODB_BACKEND_INIT.
- *
- * @param backend the `git_odb_backend` struct to initialize.
- * @param version Version the struct; pass `GIT_ODB_BACKEND_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_odb_init_backend(
- git_odb_backend *backend,
- unsigned int version);
-
-GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len);
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_openssl_h__
-#define INCLUDE_git_openssl_h__
-
-#include "git2/common.h"
-
-GIT_BEGIN_DECL
-
-/**
- * Initialize the OpenSSL locks
- *
- * OpenSSL requires the application to determine how it performs
- * locking.
- *
- * This is a last-resort convenience function which libgit2 provides for
- * allocating and initializing the locks as well as setting the
- * locking function to use the system's native locking functions.
- *
- * The locking function will be cleared and the memory will be freed
- * when you call git_threads_sutdown().
- *
- * If your programming language has an OpenSSL package/bindings, it
- * likely sets up locking. You should very strongly prefer that over
- * this function.
- *
- * @return 0 on success, -1 if there are errors or if libgit2 was not
- * built with OpenSSL and threading support.
- */
-GIT_EXTERN(int) git_openssl_set_locking(void);
-
-GIT_END_DECL
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_refdb_backend_h__
-#define INCLUDE_sys_git_refdb_backend_h__
-
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/oid.h"
-
-/**
- * @file git2/refdb_backend.h
- * @brief Git custom refs backend functions
- * @defgroup git_refdb_backend Git custom refs backend API
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-
-/**
- * Every backend's iterator must have a pointer to itself as the first
- * element, so the API can talk to it. You'd define your iterator as
- *
- * struct my_iterator {
- * git_reference_iterator parent;
- * ...
- * }
- *
- * and assign `iter->parent.backend` to your `git_refdb_backend`.
- */
-struct git_reference_iterator {
- git_refdb *db;
-
- /**
- * Return the current reference and advance the iterator.
- */
- int (*next)(
- git_reference **ref,
- git_reference_iterator *iter);
-
- /**
- * Return the name of the current reference and advance the iterator
- */
- int (*next_name)(
- const char **ref_name,
- git_reference_iterator *iter);
-
- /**
- * Free the iterator
- */
- void (*free)(
- git_reference_iterator *iter);
-};
-
-/** An instance for a custom backend */
-struct git_refdb_backend {
- unsigned int version;
-
- /**
- * Queries the refdb backend to determine if the given ref_name
- * exists. A refdb implementation must provide this function.
- */
- int (*exists)(
- int *exists,
- git_refdb_backend *backend,
- const char *ref_name);
-
- /**
- * Queries the refdb backend for a given reference. A refdb
- * implementation must provide this function.
- */
- int (*lookup)(
- git_reference **out,
- git_refdb_backend *backend,
- const char *ref_name);
-
- /**
- * Allocate an iterator object for the backend.
- *
- * A refdb implementation must provide this function.
- */
- int (*iterator)(
- git_reference_iterator **iter,
- struct git_refdb_backend *backend,
- const char *glob);
-
- /*
- * Writes the given reference to the refdb. A refdb implementation
- * must provide this function.
- */
- int (*write)(git_refdb_backend *backend,
- const git_reference *ref, int force,
- const git_signature *who, const char *message,
- const git_oid *old, const char *old_target);
-
- int (*rename)(
- git_reference **out, git_refdb_backend *backend,
- const char *old_name, const char *new_name, int force,
- const git_signature *who, const char *message);
-
- /**
- * Deletes the given reference (and if necessary its reflog)
- * from the refdb. A refdb implementation must provide this
- * function.
- */
- int (*del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target);
-
- /**
- * Suggests that the given refdb compress or optimize its references.
- * This mechanism is implementation specific. (For on-disk reference
- * databases, this may pack all loose references.) A refdb
- * implementation may provide this function; if it is not provided,
- * nothing will be done.
- */
- int (*compress)(git_refdb_backend *backend);
-
- /**
- * Query whether a particular reference has a log (may be empty)
- */
- int (*has_log)(git_refdb_backend *backend, const char *refname);
-
- /**
- * Make sure a particular reference will have a reflog which
- * will be appended to on writes.
- */
- int (*ensure_log)(git_refdb_backend *backend, const char *refname);
-
- /**
- * Frees any resources held by the refdb (including the `git_refdb_backend`
- * itself). A refdb backend implementation must provide this function.
- */
- void (*free)(git_refdb_backend *backend);
-
- /**
- * Read the reflog for the given reference name.
- */
- int (*reflog_read)(git_reflog **out, git_refdb_backend *backend, const char *name);
-
- /**
- * Write a reflog to disk.
- */
- int (*reflog_write)(git_refdb_backend *backend, git_reflog *reflog);
-
- /**
- * Rename a reflog
- */
- int (*reflog_rename)(git_refdb_backend *_backend, const char *old_name, const char *new_name);
-
- /**
- * Remove a reflog.
- */
- int (*reflog_delete)(git_refdb_backend *backend, const char *name);
-
- /**
- * Lock a reference. The opaque parameter will be passed to the unlock function
- */
- int (*lock)(void **payload_out, git_refdb_backend *backend, const char *refname);
-
- /**
- * Unlock a reference. Only one of target or symbolic_target
- * will be set. success indicates whether to update the
- * reference or discard the lock (if it's false)
- */
- int (*unlock)(git_refdb_backend *backend, void *payload, int success, int update_reflog,
- const git_reference *ref, const git_signature *sig, const char *message);
-};
-
-#define GIT_REFDB_BACKEND_VERSION 1
-#define GIT_REFDB_BACKEND_INIT {GIT_REFDB_BACKEND_VERSION}
-
-/**
- * Initializes a `git_refdb_backend` with default values. Equivalent to
- * creating an instance with GIT_REFDB_BACKEND_INIT.
- *
- * @param backend the `git_refdb_backend` struct to initialize
- * @param version Version of struct; pass `GIT_REFDB_BACKEND_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_refdb_init_backend(
- git_refdb_backend *backend,
- unsigned int version);
-
-/**
- * Constructors for default filesystem-based refdb backend
- *
- * Under normal usage, this is called for you when the repository is
- * opened / created, but you can use this to explicitly construct a
- * filesystem refdb backend for a repository.
- *
- * @param backend_out Output pointer to the git_refdb_backend object
- * @param repo Git repository to access
- * @return 0 on success, <0 error code on failure
- */
-GIT_EXTERN(int) git_refdb_backend_fs(
- git_refdb_backend **backend_out,
- git_repository *repo);
-
-/**
- * Sets the custom backend to an existing reference DB
- *
- * The `git_refdb` will take ownership of the `git_refdb_backend` so you
- * should NOT free it after calling this function.
- *
- * @param refdb database to add the backend to
- * @param backend pointer to a git_refdb_backend instance
- * @return 0 on success; error code otherwise
- */
-GIT_EXTERN(int) git_refdb_set_backend(
- git_refdb *refdb,
- git_refdb_backend *backend);
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_reflog_h__
-#define INCLUDE_sys_git_reflog_h__
-
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/oid.h"
-
-GIT_BEGIN_DECL
-
-GIT_EXTERN(git_reflog_entry *) git_reflog_entry__alloc(void);
-GIT_EXTERN(void) git_reflog_entry__free(git_reflog_entry *entry);
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_refdb_h__
-#define INCLUDE_sys_git_refdb_h__
-
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/oid.h"
-
-/**
- * @file git2/sys/refs.h
- * @brief Low-level Git ref creation
- * @defgroup git_backend Git custom backend APIs
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Create a new direct reference from an OID.
- *
- * @param name the reference name
- * @param oid the object id for a direct reference
- * @param peel the first non-tag object's OID, or NULL
- * @return the created git_reference or NULL on error
- */
-GIT_EXTERN(git_reference *) git_reference__alloc(
- const char *name,
- const git_oid *oid,
- const git_oid *peel);
-
-/**
- * Create a new symbolic reference.
- *
- * @param name the reference name
- * @param target the target for a symbolic reference
- * @return the created git_reference or NULL on error
- */
-GIT_EXTERN(git_reference *) git_reference__alloc_symbolic(
- const char *name,
- const char *target);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_sys_git_transport_h
-#define INCLUDE_sys_git_transport_h
-
-#include "git2/net.h"
-#include "git2/types.h"
-
-GIT_BEGIN_DECL
-
-GIT_END_DECL
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_repository_h__
-#define INCLUDE_sys_git_repository_h__
-
-#include "git2/common.h"
-#include "git2/types.h"
-
-/**
- * @file git2/sys/repository.h
- * @brief Git repository custom implementation routines
- * @defgroup git_backend Git custom backend APIs
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Create a new repository with neither backends nor config object
- *
- * Note that this is only useful if you wish to associate the repository
- * with a non-filesystem-backed object database and config store.
- *
- * @param out The blank repository
- * @return 0 on success, or an error code
- */
-GIT_EXTERN(int) git_repository_new(git_repository **out);
-
-/**
- * Reset all the internal state in a repository.
- *
- * This will free all the mapped memory and internal objects
- * of the repository and leave it in a "blank" state.
- *
- * There's no need to call this function directly unless you're
- * trying to aggressively cleanup the repo before its
- * deallocation. `git_repository_free` already performs this operation
- * before deallocation the repo.
- */
-GIT_EXTERN(void) git_repository__cleanup(git_repository *repo);
-
-/**
- * Update the filesystem config settings for an open repository
- *
- * When a repository is initialized, config values are set based on the
- * properties of the filesystem that the repository is on, such as
- * "core.ignorecase", "core.filemode", "core.symlinks", etc. If the
- * repository is moved to a new filesystem, these properties may no
- * longer be correct and API calls may not behave as expected. This
- * call reruns the phase of repository initialization that sets those
- * properties to compensate for the current filesystem of the repo.
- *
- * @param repo A repository object
- * @param recurse_submodules Should submodules be updated recursively
- * @return 0 on success, < 0 on error
- */
-GIT_EXTERN(int) git_repository_reinit_filesystem(
- git_repository *repo,
- int recurse_submodules);
-
-/**
- * Set the configuration file for this repository
- *
- * This configuration file will be used for all configuration
- * queries involving this repository.
- *
- * The repository will keep a reference to the config file;
- * the user must still free the config after setting it
- * to the repository, or it will leak.
- *
- * @param repo A repository object
- * @param config A Config object
- */
-GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *config);
-
-/**
- * Set the Object Database for this repository
- *
- * The ODB will be used for all object-related operations
- * involving this repository.
- *
- * The repository will keep a reference to the ODB; the user
- * must still free the ODB object after setting it to the
- * repository, or it will leak.
- *
- * @param repo A repository object
- * @param odb An ODB object
- */
-GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb);
-
-/**
- * Set the Reference Database Backend for this repository
- *
- * The refdb will be used for all reference related operations
- * involving this repository.
- *
- * The repository will keep a reference to the refdb; the user
- * must still free the refdb object after setting it to the
- * repository, or it will leak.
- *
- * @param repo A repository object
- * @param refdb An refdb object
- */
-GIT_EXTERN(void) git_repository_set_refdb(git_repository *repo, git_refdb *refdb);
-
-/**
- * Set the index file for this repository
- *
- * This index will be used for all index-related operations
- * involving this repository.
- *
- * The repository will keep a reference to the index file;
- * the user must still free the index after setting it
- * to the repository, or it will leak.
- *
- * @param repo A repository object
- * @param index An index object
- */
-GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index);
-
-/**
- * Set a repository to be bare.
- *
- * Clear the working directory and set core.bare to true. You may also
- * want to call `git_repository_set_index(repo, NULL)` since a bare repo
- * typically does not have an index, but this function will not do that
- * for you.
- *
- * @param repo Repo to make bare
- * @return 0 on success, <0 on failure
- */
-GIT_EXTERN(int) git_repository_set_bare(git_repository *repo);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sys_git_stream_h__
-#define INCLUDE_sys_git_stream_h__
-
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/proxy.h"
-
-GIT_BEGIN_DECL
-
-#define GIT_STREAM_VERSION 1
-
-/**
- * Every stream must have this struct as its first element, so the
- * API can talk to it. You'd define your stream as
- *
- * struct my_stream {
- * git_stream parent;
- * ...
- * }
- *
- * and fill the functions
- */
-typedef struct git_stream {
- int version;
-
- int encrypted;
- int proxy_support;
- int (*connect)(struct git_stream *);
- int (*certificate)(git_cert **, struct git_stream *);
- int (*set_proxy)(struct git_stream *, const git_proxy_options *proxy_opts);
- ssize_t (*read)(struct git_stream *, void *, size_t);
- ssize_t (*write)(struct git_stream *, const char *, size_t, int);
- int (*close)(struct git_stream *);
- void (*free)(struct git_stream *);
-} git_stream;
-
-typedef int (*git_stream_cb)(git_stream **out, const char *host, const char *port);
-
-/**
- * Register a TLS stream constructor for the library to use
- *
- * If a constructor is already set, it will be overwritten. Pass
- * `NULL` in order to deregister the current constructor.
- *
- * @param ctor the constructor to use
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_stream_register_tls(git_stream_cb ctor);
-
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_time_h__
-#define INCLUDE_git_time_h__
-
-#include "git2/common.h"
-
-GIT_BEGIN_DECL
-
-/**
- * Return a monotonic time value, useful for measuring running time
- * and setting up timeouts.
- *
- * The returned value is an arbitrary point in time -- it can only be
- * used when comparing it to another `git_time_monotonic` call.
- *
- * The time is returned in seconds, with a decimal fraction that differs
- * on accuracy based on the underlying system, but should be least
- * accurate to Nanoseconds.
- *
- * This function cannot fail.
- */
-GIT_EXTERN(double) git_time_monotonic(void);
-
-GIT_END_DECL
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_sys_git_transport_h
-#define INCLUDE_sys_git_transport_h
-
-#include "git2/net.h"
-#include "git2/types.h"
-#include "git2/strarray.h"
-#include "git2/proxy.h"
-
-/**
- * @file git2/sys/transport.h
- * @brief Git custom transport registration interfaces and functions
- * @defgroup git_transport Git custom transport registration
- * @ingroup Git
- * @{
- */
-
-GIT_BEGIN_DECL
-
-/**
- * Flags to pass to transport
- *
- * Currently unused.
- */
-typedef enum {
- GIT_TRANSPORTFLAGS_NONE = 0,
-} git_transport_flags_t;
-
-struct git_transport {
- unsigned int version;
- /* Set progress and error callbacks */
- int (*set_callbacks)(
- git_transport *transport,
- git_transport_message_cb progress_cb,
- git_transport_message_cb error_cb,
- git_transport_certificate_check_cb certificate_check_cb,
- void *payload);
-
- /* Set custom headers for HTTP requests */
- int (*set_custom_headers)(
- git_transport *transport,
- const git_strarray *custom_headers);
-
- /* Connect the transport to the remote repository, using the given
- * direction. */
- int (*connect)(
- git_transport *transport,
- const char *url,
- git_cred_acquire_cb cred_acquire_cb,
- void *cred_acquire_payload,
- const git_proxy_options *proxy_opts,
- int direction,
- int flags);
-
- /* This function may be called after a successful call to
- * connect(). The array returned is owned by the transport and
- * is guaranteed until the next call of a transport function. */
- int (*ls)(
- const git_remote_head ***out,
- size_t *size,
- git_transport *transport);
-
- /* Executes the push whose context is in the git_push object. */
- int(*push)(git_transport *transport, git_push *push, const git_remote_callbacks *callbacks);
-
- /* This function may be called after a successful call to connect(), when
- * the direction is FETCH. The function performs a negotiation to calculate
- * the wants list for the fetch. */
- int (*negotiate_fetch)(
- git_transport *transport,
- git_repository *repo,
- const git_remote_head * const *refs,
- size_t count);
-
- /* This function may be called after a successful call to negotiate_fetch(),
- * when the direction is FETCH. This function retrieves the pack file for
- * the fetch from the remote end. */
- int (*download_pack)(
- git_transport *transport,
- git_repository *repo,
- git_transfer_progress *stats,
- git_transfer_progress_cb progress_cb,
- void *progress_payload);
-
- /* Checks to see if the transport is connected */
- int (*is_connected)(git_transport *transport);
-
- /* Reads the flags value previously passed into connect() */
- int (*read_flags)(git_transport *transport, int *flags);
-
- /* Cancels any outstanding transport operation */
- void (*cancel)(git_transport *transport);
-
- /* This function is the reverse of connect() -- it terminates the
- * connection to the remote end. */
- int (*close)(git_transport *transport);
-
- /* Frees/destructs the git_transport object. */
- void (*free)(git_transport *transport);
-};
-
-#define GIT_TRANSPORT_VERSION 1
-#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION}
-
-/**
- * Initializes a `git_transport` with default values. Equivalent to
- * creating an instance with GIT_TRANSPORT_INIT.
- *
- * @param opts the `git_transport` struct to initialize
- * @param version Version of struct; pass `GIT_TRANSPORT_VERSION`
- * @return Zero on success; -1 on failure.
- */
-GIT_EXTERN(int) git_transport_init(
- git_transport *opts,
- unsigned int version);
-
-/**
- * Function to use to create a transport from a URL. The transport database
- * is scanned to find a transport that implements the scheme of the URI (i.e.
- * git:// or http://) and a transport object is returned to the caller.
- *
- * @param out The newly created transport (out)
- * @param owner The git_remote which will own this transport
- * @param url The URL to connect to
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url);
-
-/**
- * Create an ssh transport with custom git command paths
- *
- * This is a factory function suitable for setting as the transport
- * callback in a remote (or for a clone in the options).
- *
- * The payload argument must be a strarray pointer with the paths for
- * the `git-upload-pack` and `git-receive-pack` at index 0 and 1.
- *
- * @param out the resulting transport
- * @param owner the owning remote
- * @param payload a strarray with the paths
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload);
-
-/**
- * Add a custom transport definition, to be used in addition to the built-in
- * set of transports that come with libgit2.
- *
- * The caller is responsible for synchronizing calls to git_transport_register
- * and git_transport_unregister with other calls to the library that
- * instantiate transports.
- *
- * @param prefix The scheme (ending in "://") to match, i.e. "git://"
- * @param cb The callback used to create an instance of the transport
- * @param param A fixed parameter to pass to cb at creation time
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_register(
- const char *prefix,
- git_transport_cb cb,
- void *param);
-
-/**
- *
- * Unregister a custom transport definition which was previously registered
- * with git_transport_register.
- *
- * @param prefix From the previous call to git_transport_register
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_unregister(
- const char *prefix);
-
-/* Transports which come with libgit2 (match git_transport_cb). The expected
- * value for "param" is listed in-line below. */
-
-/**
- * Create an instance of the dummy transport.
- *
- * @param out The newly created transport (out)
- * @param owner The git_remote which will own this transport
- * @param payload You must pass NULL for this parameter.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_dummy(
- git_transport **out,
- git_remote *owner,
- /* NULL */ void *payload);
-
-/**
- * Create an instance of the local transport.
- *
- * @param out The newly created transport (out)
- * @param owner The git_remote which will own this transport
- * @param payload You must pass NULL for this parameter.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_local(
- git_transport **out,
- git_remote *owner,
- /* NULL */ void *payload);
-
-/**
- * Create an instance of the smart transport.
- *
- * @param out The newly created transport (out)
- * @param owner The git_remote which will own this transport
- * @param payload A pointer to a git_smart_subtransport_definition
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transport_smart(
- git_transport **out,
- git_remote *owner,
- /* (git_smart_subtransport_definition *) */ void *payload);
-
-/**
- * Call the certificate check for this transport.
- *
- * @param transport a smart transport
- * @param cert the certificate to pass to the caller
- * @param valid whether we believe the certificate is valid
- * @param hostname the hostname we connected to
- * @return the return value of the callback
- */
-GIT_EXTERN(int) git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname);
-
-/**
- * Call the credentials callback for this transport
- *
- * @param out the pointer where the creds are to be stored
- * @param transport a smart transport
- * @param user the user we saw on the url (if any)
- * @param methods available methods for authentication
- * @return the return value of the callback
- */
-GIT_EXTERN(int) git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods);
-
-/*
- *** End of base transport interface ***
- *** Begin interface for subtransports for the smart transport ***
- */
-
-/* The smart transport knows how to speak the git protocol, but it has no
- * knowledge of how to establish a connection between it and another endpoint,
- * or how to move data back and forth. For this, a subtransport interface is
- * declared, and the smart transport delegates this work to the subtransports.
- * Three subtransports are implemented: git, http, and winhttp. (The http and
- * winhttp transports each implement both http and https.) */
-
-/* Subtransports can either be RPC = 0 (persistent connection) or RPC = 1
- * (request/response). The smart transport handles the differences in its own
- * logic. The git subtransport is RPC = 0, while http and winhttp are both
- * RPC = 1. */
-
-/* Actions that the smart transport can ask
- * a subtransport to perform */
-typedef enum {
- GIT_SERVICE_UPLOADPACK_LS = 1,
- GIT_SERVICE_UPLOADPACK = 2,
- GIT_SERVICE_RECEIVEPACK_LS = 3,
- GIT_SERVICE_RECEIVEPACK = 4,
-} git_smart_service_t;
-
-typedef struct git_smart_subtransport git_smart_subtransport;
-typedef struct git_smart_subtransport_stream git_smart_subtransport_stream;
-
-/* A stream used by the smart transport to read and write data
- * from a subtransport */
-struct git_smart_subtransport_stream {
- /* The owning subtransport */
- git_smart_subtransport *subtransport;
-
- int (*read)(
- git_smart_subtransport_stream *stream,
- char *buffer,
- size_t buf_size,
- size_t *bytes_read);
-
- int (*write)(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len);
-
- void (*free)(
- git_smart_subtransport_stream *stream);
-};
-
-/* An implementation of a subtransport which carries data for the
- * smart transport */
-struct git_smart_subtransport {
- int (* action)(
- git_smart_subtransport_stream **out,
- git_smart_subtransport *transport,
- const char *url,
- git_smart_service_t action);
-
- /* Subtransports are guaranteed a call to close() between
- * calls to action(), except for the following two "natural" progressions
- * of actions against a constant URL.
- *
- * 1. UPLOADPACK_LS -> UPLOADPACK
- * 2. RECEIVEPACK_LS -> RECEIVEPACK */
- int (*close)(git_smart_subtransport *transport);
-
- void (*free)(git_smart_subtransport *transport);
-};
-
-/* A function which creates a new subtransport for the smart transport */
-typedef int (*git_smart_subtransport_cb)(
- git_smart_subtransport **out,
- git_transport* owner,
- void* param);
-
-/**
- * Definition for a "subtransport"
- *
- * This is used to let the smart protocol code know about the protocol
- * which you are implementing.
- */
-typedef struct git_smart_subtransport_definition {
- /** The function to use to create the git_smart_subtransport */
- git_smart_subtransport_cb callback;
-
- /**
- * True if the protocol is stateless; false otherwise. For example,
- * http:// is stateless, but git:// is not.
- */
- unsigned rpc;
-
- /** Param of the callback
- */
- void* param;
-} git_smart_subtransport_definition;
-
-/* Smart transport subtransports that come with libgit2 */
-
-/**
- * Create an instance of the http subtransport. This subtransport
- * also supports https. On Win32, this subtransport may be implemented
- * using the WinHTTP library.
- *
- * @param out The newly created subtransport
- * @param owner The smart transport to own this subtransport
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_smart_subtransport_http(
- git_smart_subtransport **out,
- git_transport* owner,
- void *param);
-
-/**
- * Create an instance of the git subtransport.
- *
- * @param out The newly created subtransport
- * @param owner The smart transport to own this subtransport
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_smart_subtransport_git(
- git_smart_subtransport **out,
- git_transport* owner,
- void *param);
-
-/**
- * Create an instance of the ssh subtransport.
- *
- * @param out The newly created subtransport
- * @param owner The smart transport to own this subtransport
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_smart_subtransport_ssh(
- git_smart_subtransport **out,
- git_transport* owner,
- void *param);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_tag_h__
-#define INCLUDE_git_tag_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "object.h"
-#include "strarray.h"
-
-/**
- * @file git2/tag.h
- * @brief Git tag parsing routines
- * @defgroup git_tag Git tag management
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Lookup a tag object from the repository.
- *
- * @param out pointer to the looked up tag
- * @param repo the repo to use when locating the tag.
- * @param id identity of the tag to locate.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tag_lookup(
- git_tag **out, git_repository *repo, const git_oid *id);
-
-/**
- * Lookup a tag object from the repository,
- * given a prefix of its identifier (short id).
- *
- * @see git_object_lookup_prefix
- *
- * @param out pointer to the looked up tag
- * @param repo the repo to use when locating the tag.
- * @param id identity of the tag to locate.
- * @param len the length of the short identifier
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tag_lookup_prefix(
- git_tag **out, git_repository *repo, const git_oid *id, size_t len);
-
-/**
- * Close an open tag
- *
- * You can no longer use the git_tag pointer after this call.
- *
- * IMPORTANT: You MUST call this method when you are through with a tag to
- * release memory. Failure to do so will cause a memory leak.
- *
- * @param tag the tag to close
- */
-GIT_EXTERN(void) git_tag_free(git_tag *tag);
-
-/**
- * Get the id of a tag.
- *
- * @param tag a previously loaded tag.
- * @return object identity for the tag.
- */
-GIT_EXTERN(const git_oid *) git_tag_id(const git_tag *tag);
-
-/**
- * Get the repository that contains the tag.
- *
- * @param tag A previously loaded tag.
- * @return Repository that contains this tag.
- */
-GIT_EXTERN(git_repository *) git_tag_owner(const git_tag *tag);
-
-/**
- * Get the tagged object of a tag
- *
- * This method performs a repository lookup for the
- * given object and returns it
- *
- * @param target_out pointer where to store the target
- * @param tag a previously loaded tag.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tag_target(git_object **target_out, const git_tag *tag);
-
-/**
- * Get the OID of the tagged object of a tag
- *
- * @param tag a previously loaded tag.
- * @return pointer to the OID
- */
-GIT_EXTERN(const git_oid *) git_tag_target_id(const git_tag *tag);
-
-/**
- * Get the type of a tag's tagged object
- *
- * @param tag a previously loaded tag.
- * @return type of the tagged object
- */
-GIT_EXTERN(git_otype) git_tag_target_type(const git_tag *tag);
-
-/**
- * Get the name of a tag
- *
- * @param tag a previously loaded tag.
- * @return name of the tag
- */
-GIT_EXTERN(const char *) git_tag_name(const git_tag *tag);
-
-/**
- * Get the tagger (author) of a tag
- *
- * @param tag a previously loaded tag.
- * @return reference to the tag's author or NULL when unspecified
- */
-GIT_EXTERN(const git_signature *) git_tag_tagger(const git_tag *tag);
-
-/**
- * Get the message of a tag
- *
- * @param tag a previously loaded tag.
- * @return message of the tag or NULL when unspecified
- */
-GIT_EXTERN(const char *) git_tag_message(const git_tag *tag);
-
-
-/**
- * Create a new tag in the repository from an object
- *
- * A new reference will also be created pointing to
- * this tag object. If `force` is true and a reference
- * already exists with the given name, it'll be replaced.
- *
- * The message will not be cleaned up. This can be achieved
- * through `git_message_prettify()`.
- *
- * The tag name will be checked for validity. You must avoid
- * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
- * sequences ".." and "@{" which have special meaning to revparse.
- *
- * @param oid Pointer where to store the OID of the
- * newly created tag. If the tag already exists, this parameter
- * will be the oid of the existing tag, and the function will
- * return a GIT_EEXISTS error code.
- *
- * @param repo Repository where to store the tag
- *
- * @param tag_name Name for the tag; this name is validated
- * for consistency. It should also not conflict with an
- * already existing tag name
- *
- * @param target Object to which this tag points. This object
- * must belong to the given `repo`.
- *
- * @param tagger Signature of the tagger for this tag, and
- * of the tagging time
- *
- * @param message Full message for this tag
- *
- * @param force Overwrite existing references
- *
- * @return 0 on success, GIT_EINVALIDSPEC or an error code
- * A tag object is written to the ODB, and a proper reference
- * is written in the /refs/tags folder, pointing to it
- */
-GIT_EXTERN(int) git_tag_create(
- git_oid *oid,
- git_repository *repo,
- const char *tag_name,
- const git_object *target,
- const git_signature *tagger,
- const char *message,
- int force);
-
-/**
- * Create a new tag in the object database pointing to a git_object
- *
- * The message will not be cleaned up. This can be achieved
- * through `git_message_prettify()`.
- *
- * @param oid Pointer where to store the OID of the
- * newly created tag
- *
- * @param repo Repository where to store the tag
- *
- * @param tag_name Name for the tag
- *
- * @param target Object to which this tag points. This object
- * must belong to the given `repo`.
- *
- * @param tagger Signature of the tagger for this tag, and
- * of the tagging time
- *
- * @param message Full message for this tag
- *
- * @return 0 on success or an error code
- */
-GIT_EXTERN(int) git_tag_annotation_create(
- git_oid *oid,
- git_repository *repo,
- const char *tag_name,
- const git_object *target,
- const git_signature *tagger,
- const char *message);
-
-/**
- * Create a new tag in the repository from a buffer
- *
- * @param oid Pointer where to store the OID of the newly created tag
- * @param repo Repository where to store the tag
- * @param buffer Raw tag data
- * @param force Overwrite existing tags
- * @return 0 on success; error code otherwise
- */
-GIT_EXTERN(int) git_tag_create_frombuffer(
- git_oid *oid,
- git_repository *repo,
- const char *buffer,
- int force);
-
-/**
- * Create a new lightweight tag pointing at a target object
- *
- * A new direct reference will be created pointing to
- * this target object. If `force` is true and a reference
- * already exists with the given name, it'll be replaced.
- *
- * The tag name will be checked for validity.
- * See `git_tag_create()` for rules about valid names.
- *
- * @param oid Pointer where to store the OID of the provided
- * target object. If the tag already exists, this parameter
- * will be filled with the oid of the existing pointed object
- * and the function will return a GIT_EEXISTS error code.
- *
- * @param repo Repository where to store the lightweight tag
- *
- * @param tag_name Name for the tag; this name is validated
- * for consistency. It should also not conflict with an
- * already existing tag name
- *
- * @param target Object to which this tag points. This object
- * must belong to the given `repo`.
- *
- * @param force Overwrite existing references
- *
- * @return 0 on success, GIT_EINVALIDSPEC or an error code
- * A proper reference is written in the /refs/tags folder,
- * pointing to the provided target object
- */
-GIT_EXTERN(int) git_tag_create_lightweight(
- git_oid *oid,
- git_repository *repo,
- const char *tag_name,
- const git_object *target,
- int force);
-
-/**
- * Delete an existing tag reference.
- *
- * The tag name will be checked for validity.
- * See `git_tag_create()` for rules about valid names.
- *
- * @param repo Repository where lives the tag
- *
- * @param tag_name Name of the tag to be deleted;
- * this name is validated for consistency.
- *
- * @return 0 on success, GIT_EINVALIDSPEC or an error code
- */
-GIT_EXTERN(int) git_tag_delete(
- git_repository *repo,
- const char *tag_name);
-
-/**
- * Fill a list with all the tags in the Repository
- *
- * The string array will be filled with the names of the
- * matching tags; these values are owned by the user and
- * should be free'd manually when no longer needed, using
- * `git_strarray_free`.
- *
- * @param tag_names Pointer to a git_strarray structure where
- * the tag names will be stored
- * @param repo Repository where to find the tags
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tag_list(
- git_strarray *tag_names,
- git_repository *repo);
-
-/**
- * Fill a list with all the tags in the Repository
- * which name match a defined pattern
- *
- * If an empty pattern is provided, all the tags
- * will be returned.
- *
- * The string array will be filled with the names of the
- * matching tags; these values are owned by the user and
- * should be free'd manually when no longer needed, using
- * `git_strarray_free`.
- *
- * @param tag_names Pointer to a git_strarray structure where
- * the tag names will be stored
- * @param pattern Standard fnmatch pattern
- * @param repo Repository where to find the tags
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tag_list_match(
- git_strarray *tag_names,
- const char *pattern,
- git_repository *repo);
-
-
-typedef int (*git_tag_foreach_cb)(const char *name, git_oid *oid, void *payload);
-
-/**
- * Call callback `cb' for each tag in the repository
- *
- * @param repo Repository
- * @param callback Callback function
- * @param payload Pointer to callback data (optional)
- */
-GIT_EXTERN(int) git_tag_foreach(
- git_repository *repo,
- git_tag_foreach_cb callback,
- void *payload);
-
-
-/**
- * Recursively peel a tag until a non tag git_object is found
- *
- * The retrieved `tag_target` object is owned by the repository
- * and should be closed with the `git_object_free` method.
- *
- * @param tag_target_out Pointer to the peeled git_object
- * @param tag The tag to be processed
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tag_peel(
- git_object **tag_target_out,
- const git_tag *tag);
-
-/**
- * Create an in-memory copy of a tag. The copy must be explicitly
- * free'd or it will leak.
- *
- * @param out Pointer to store the copy of the tag
- * @param source Original tag to copy
- */
-GIT_EXTERN(int) git_tag_dup(git_tag **out, git_tag *source);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_trace_h__
-#define INCLUDE_git_trace_h__
-
-#include "common.h"
-#include "types.h"
-
-/**
- * @file git2/trace.h
- * @brief Git tracing configuration routines
- * @defgroup git_trace Git tracing configuration routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Available tracing levels. When tracing is set to a particular level,
- * callers will be provided tracing at the given level and all lower levels.
- */
-typedef enum {
- /** No tracing will be performed. */
- GIT_TRACE_NONE = 0,
-
- /** Severe errors that may impact the program's execution */
- GIT_TRACE_FATAL = 1,
-
- /** Errors that do not impact the program's execution */
- GIT_TRACE_ERROR = 2,
-
- /** Warnings that suggest abnormal data */
- GIT_TRACE_WARN = 3,
-
- /** Informational messages about program execution */
- GIT_TRACE_INFO = 4,
-
- /** Detailed data that allows for debugging */
- GIT_TRACE_DEBUG = 5,
-
- /** Exceptionally detailed debugging data */
- GIT_TRACE_TRACE = 6
-} git_trace_level_t;
-
-/**
- * An instance for a tracing function
- */
-typedef void (*git_trace_callback)(git_trace_level_t level, const char *msg);
-
-/**
- * Sets the system tracing configuration to the specified level with the
- * specified callback. When system events occur at a level equal to, or
- * lower than, the given level they will be reported to the given callback.
- *
- * @param level Level to set tracing to
- * @param cb Function to call with trace data
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_trace_set(git_trace_level_t level, git_trace_callback cb);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_transaction_h__
-#define INCLUDE_git_transaction_h__
-
-#include "common.h"
-
-/**
- * @file git2/transaction.h
- * @brief Git transactional reference routines
- * @defgroup git_transaction Git transactional reference routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Create a new transaction object
- *
- * This does not lock anything, but sets up the transaction object to
- * know from which repository to lock.
- *
- * @param out the resulting transaction
- * @param repo the repository in which to lock
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transaction_new(git_transaction **out, git_repository *repo);
-
-/**
- * Lock a reference
- *
- * Lock the specified reference. This is the first step to updating a
- * reference.
- *
- * @param tx the transaction
- * @param refname the reference to lock
- * @return 0 or an error message
- */
-GIT_EXTERN(int) git_transaction_lock_ref(git_transaction *tx, const char *refname);
-
-/**
- * Set the target of a reference
- *
- * Set the target of the specified reference. This reference must be
- * locked.
- *
- * @param tx the transaction
- * @param refname reference to update
- * @param target target to set the reference to
- * @param sig signature to use in the reflog; pass NULL to read the identity from the config
- * @param msg message to use in the reflog
- * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
- */
-GIT_EXTERN(int) git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg);
-
-/**
- * Set the target of a reference
- *
- * Set the target of the specified reference. This reference must be
- * locked.
- *
- * @param tx the transaction
- * @param refname reference to update
- * @param target target to set the reference to
- * @param sig signature to use in the reflog; pass NULL to read the identity from the config
- * @param msg message to use in the reflog
- * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
- */
-GIT_EXTERN(int) git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg);
-
-/**
- * Set the reflog of a reference
- *
- * Set the specified reference's reflog. If this is combined with
- * setting the target, that update won't be written to the reflog.
- *
- * @param tx the transaction
- * @param refname the reference whose reflog to set
- * @param reflog the reflog as it should be written out
- * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
- */
-GIT_EXTERN(int) git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog);
-
-/**
- * Remove a reference
- *
- * @param tx the transaction
- * @param refname the reference to remove
- * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
- */
-GIT_EXTERN(int) git_transaction_remove(git_transaction *tx, const char *refname);
-
-/**
- * Commit the changes from the transaction
- *
- * Perform the changes that have been queued. The updates will be made
- * one by one, and the first failure will stop the processing.
- *
- * @param tx the transaction
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_transaction_commit(git_transaction *tx);
-
-/**
- * Free the resources allocated by this transaction
- *
- * If any references remain locked, they will be unlocked without any
- * changes made to them.
- *
- * @param tx the transaction
- */
-GIT_EXTERN(void) git_transaction_free(git_transaction *tx);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_transport_h__
-#define INCLUDE_git_transport_h__
-
-#include "indexer.h"
-#include "net.h"
-#include "types.h"
-
-/**
- * @file git2/transport.h
- * @brief Git transport interfaces and functions
- * @defgroup git_transport interfaces and functions
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/** Signature of a function which creates a transport */
-typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param);
-
-/**
- * Type of SSH host fingerprint
- */
-typedef enum {
- /** MD5 is available */
- GIT_CERT_SSH_MD5 = (1 << 0),
- /** SHA-1 is available */
- GIT_CERT_SSH_SHA1 = (1 << 1),
-} git_cert_ssh_t;
-
-/**
- * Hostkey information taken from libssh2
- */
-typedef struct {
- git_cert parent;
-
- /**
- * A hostkey type from libssh2, either
- * `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1`
- */
- git_cert_ssh_t type;
-
- /**
- * Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will
- * have the MD5 hash of the hostkey.
- */
- unsigned char hash_md5[16];
-
- /**
- * Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will
- * have the SHA-1 hash of the hostkey.
- */
- unsigned char hash_sha1[20];
-} git_cert_hostkey;
-
-/**
- * X.509 certificate information
- */
-typedef struct {
- git_cert parent;
- /**
- * Pointer to the X.509 certificate data
- */
- void *data;
- /**
- * Length of the memory block pointed to by `data`.
- */
- size_t len;
-} git_cert_x509;
-
-/*
- *** Begin interface for credentials acquisition ***
- */
-
-/** Authentication type requested */
-typedef enum {
- /* git_cred_userpass_plaintext */
- GIT_CREDTYPE_USERPASS_PLAINTEXT = (1u << 0),
-
- /* git_cred_ssh_key */
- GIT_CREDTYPE_SSH_KEY = (1u << 1),
-
- /* git_cred_ssh_custom */
- GIT_CREDTYPE_SSH_CUSTOM = (1u << 2),
-
- /* git_cred_default */
- GIT_CREDTYPE_DEFAULT = (1u << 3),
-
- /* git_cred_ssh_interactive */
- GIT_CREDTYPE_SSH_INTERACTIVE = (1u << 4),
-
- /**
- * Username-only information
- *
- * If the SSH transport does not know which username to use,
- * it will ask via this credential type.
- */
- GIT_CREDTYPE_USERNAME = (1u << 5),
-
- /**
- * Credentials read from memory.
- *
- * Only available for libssh2+OpenSSL for now.
- */
- GIT_CREDTYPE_SSH_MEMORY = (1u << 6),
-} git_credtype_t;
-
-/* The base structure for all credential types */
-typedef struct git_cred git_cred;
-
-struct git_cred {
- git_credtype_t credtype;
- void (*free)(git_cred *cred);
-};
-
-/** A plaintext username and password */
-typedef struct {
- git_cred parent;
- char *username;
- char *password;
-} git_cred_userpass_plaintext;
-
-
-/*
- * If the user hasn't included libssh2.h before git2.h, we need to
- * define a few types for the callback signatures.
- */
-#ifndef LIBSSH2_VERSION
-typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION;
-typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT LIBSSH2_USERAUTH_KBDINT_PROMPT;
-typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE LIBSSH2_USERAUTH_KBDINT_RESPONSE;
-#endif
-
-typedef int (*git_cred_sign_callback)(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, const unsigned char *data, size_t data_len, void **abstract);
-typedef void (*git_cred_ssh_interactive_callback)(const char* name, int name_len, const char* instruction, int instruction_len, int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts, LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses, void **abstract);
-
-/**
- * A ssh key from disk
- */
-typedef struct git_cred_ssh_key {
- git_cred parent;
- char *username;
- char *publickey;
- char *privatekey;
- char *passphrase;
-} git_cred_ssh_key;
-
-/**
- * Keyboard-interactive based ssh authentication
- */
-typedef struct git_cred_ssh_interactive {
- git_cred parent;
- char *username;
- git_cred_ssh_interactive_callback prompt_callback;
- void *payload;
-} git_cred_ssh_interactive;
-
-/**
- * A key with a custom signature function
- */
-typedef struct git_cred_ssh_custom {
- git_cred parent;
- char *username;
- char *publickey;
- size_t publickey_len;
- git_cred_sign_callback sign_callback;
- void *payload;
-} git_cred_ssh_custom;
-
-/** A key for NTLM/Kerberos "default" credentials */
-typedef struct git_cred git_cred_default;
-
-/** Username-only credential information */
-typedef struct git_cred_username {
- git_cred parent;
- char username[1];
-} git_cred_username;
-
-/**
- * Check whether a credential object contains username information.
- *
- * @param cred object to check
- * @return 1 if the credential object has non-NULL username, 0 otherwise
- */
-GIT_EXTERN(int) git_cred_has_username(git_cred *cred);
-
-/**
- * Create a new plain-text username and password credential object.
- * The supplied credential parameter will be internally duplicated.
- *
- * @param out The newly created credential object.
- * @param username The username of the credential.
- * @param password The password of the credential.
- * @return 0 for success or an error code for failure
- */
-GIT_EXTERN(int) git_cred_userpass_plaintext_new(
- git_cred **out,
- const char *username,
- const char *password);
-
-/**
- * Create a new passphrase-protected ssh key credential object.
- * The supplied credential parameter will be internally duplicated.
- *
- * @param out The newly created credential object.
- * @param username username to use to authenticate
- * @param publickey The path to the public key of the credential.
- * @param privatekey The path to the private key of the credential.
- * @param passphrase The passphrase of the credential.
- * @return 0 for success or an error code for failure
- */
-GIT_EXTERN(int) git_cred_ssh_key_new(
- git_cred **out,
- const char *username,
- const char *publickey,
- const char *privatekey,
- const char *passphrase);
-
-/**
- * Create a new ssh keyboard-interactive based credential object.
- * The supplied credential parameter will be internally duplicated.
- *
- * @param username Username to use to authenticate.
- * @param prompt_callback The callback method used for prompts.
- * @param payload Additional data to pass to the callback.
- * @return 0 for success or an error code for failure.
- */
-GIT_EXTERN(int) git_cred_ssh_interactive_new(
- git_cred **out,
- const char *username,
- git_cred_ssh_interactive_callback prompt_callback,
- void *payload);
-
-/**
- * Create a new ssh key credential object used for querying an ssh-agent.
- * The supplied credential parameter will be internally duplicated.
- *
- * @param out The newly created credential object.
- * @param username username to use to authenticate
- * @return 0 for success or an error code for failure
- */
-GIT_EXTERN(int) git_cred_ssh_key_from_agent(
- git_cred **out,
- const char *username);
-
-/**
- * Create an ssh key credential with a custom signing function.
- *
- * This lets you use your own function to sign the challenge.
- *
- * This function and its credential type is provided for completeness
- * and wraps `libssh2_userauth_publickey()`, which is undocumented.
- *
- * The supplied credential parameter will be internally duplicated.
- *
- * @param out The newly created credential object.
- * @param username username to use to authenticate
- * @param publickey The bytes of the public key.
- * @param publickey_len The length of the public key in bytes.
- * @param sign_callback The callback method to sign the data during the challenge.
- * @param payload Additional data to pass to the callback.
- * @return 0 for success or an error code for failure
- */
-GIT_EXTERN(int) git_cred_ssh_custom_new(
- git_cred **out,
- const char *username,
- const char *publickey,
- size_t publickey_len,
- git_cred_sign_callback sign_callback,
- void *payload);
-
-/**
- * Create a "default" credential usable for Negotiate mechanisms like NTLM
- * or Kerberos authentication.
- *
- * @return 0 for success or an error code for failure
- */
-GIT_EXTERN(int) git_cred_default_new(git_cred **out);
-
-/**
- * Create a credential to specify a username.
- *
- * This is used with ssh authentication to query for the username if
- * none is specified in the url.
- */
-GIT_EXTERN(int) git_cred_username_new(git_cred **cred, const char *username);
-
-/**
- * Create a new ssh key credential object reading the keys from memory.
- *
- * @param out The newly created credential object.
- * @param username username to use to authenticate.
- * @param publickey The public key of the credential.
- * @param privatekey The private key of the credential.
- * @param passphrase The passphrase of the credential.
- * @return 0 for success or an error code for failure
- */
-GIT_EXTERN(int) git_cred_ssh_key_memory_new(
- git_cred **out,
- const char *username,
- const char *publickey,
- const char *privatekey,
- const char *passphrase);
-
-
-/**
- * Free a credential.
- *
- * This is only necessary if you own the object; that is, if you are a
- * transport.
- *
- * @param cred the object to free
- */
-GIT_EXTERN(void) git_cred_free(git_cred *cred);
-
-/**
- * Signature of a function which acquires a credential object.
- *
- * - cred: The newly created credential object.
- * - url: The resource for which we are demanding a credential.
- * - username_from_url: The username that was embedded in a "user\@host"
- * remote url, or NULL if not included.
- * - allowed_types: A bitmask stating which cred types are OK to return.
- * - payload: The payload provided when specifying this callback.
- * - returns 0 for success, < 0 to indicate an error, > 0 to indicate
- * no credential was acquired
- */
-typedef int (*git_cred_acquire_cb)(
- git_cred **cred,
- const char *url,
- const char *username_from_url,
- unsigned int allowed_types,
- void *payload);
-
-/** @} */
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_tree_h__
-#define INCLUDE_git_tree_h__
-
-#include "common.h"
-#include "types.h"
-#include "oid.h"
-#include "object.h"
-
-/**
- * @file git2/tree.h
- * @brief Git tree parsing, loading routines
- * @defgroup git_tree Git tree parsing, loading routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Lookup a tree object from the repository.
- *
- * @param out Pointer to the looked up tree
- * @param repo The repo to use when locating the tree.
- * @param id Identity of the tree to locate.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tree_lookup(
- git_tree **out, git_repository *repo, const git_oid *id);
-
-/**
- * Lookup a tree object from the repository,
- * given a prefix of its identifier (short id).
- *
- * @see git_object_lookup_prefix
- *
- * @param out pointer to the looked up tree
- * @param repo the repo to use when locating the tree.
- * @param id identity of the tree to locate.
- * @param len the length of the short identifier
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tree_lookup_prefix(
- git_tree **out,
- git_repository *repo,
- const git_oid *id,
- size_t len);
-
-/**
- * Close an open tree
- *
- * You can no longer use the git_tree pointer after this call.
- *
- * IMPORTANT: You MUST call this method when you stop using a tree to
- * release memory. Failure to do so will cause a memory leak.
- *
- * @param tree The tree to close
- */
-GIT_EXTERN(void) git_tree_free(git_tree *tree);
-
-/**
- * Get the id of a tree.
- *
- * @param tree a previously loaded tree.
- * @return object identity for the tree.
- */
-GIT_EXTERN(const git_oid *) git_tree_id(const git_tree *tree);
-
-/**
- * Get the repository that contains the tree.
- *
- * @param tree A previously loaded tree.
- * @return Repository that contains this tree.
- */
-GIT_EXTERN(git_repository *) git_tree_owner(const git_tree *tree);
-
-/**
- * Get the number of entries listed in a tree
- *
- * @param tree a previously loaded tree.
- * @return the number of entries in the tree
- */
-GIT_EXTERN(size_t) git_tree_entrycount(const git_tree *tree);
-
-/**
- * Lookup a tree entry by its filename
- *
- * This returns a git_tree_entry that is owned by the git_tree. You don't
- * have to free it, but you must not use it after the git_tree is released.
- *
- * @param tree a previously loaded tree.
- * @param filename the filename of the desired entry
- * @return the tree entry; NULL if not found
- */
-GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(
- const git_tree *tree, const char *filename);
-
-/**
- * Lookup a tree entry by its position in the tree
- *
- * This returns a git_tree_entry that is owned by the git_tree. You don't
- * have to free it, but you must not use it after the git_tree is released.
- *
- * @param tree a previously loaded tree.
- * @param idx the position in the entry list
- * @return the tree entry; NULL if not found
- */
-GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(
- const git_tree *tree, size_t idx);
-
-/**
- * Lookup a tree entry by SHA value.
- *
- * This returns a git_tree_entry that is owned by the git_tree. You don't
- * have to free it, but you must not use it after the git_tree is released.
- *
- * Warning: this must examine every entry in the tree, so it is not fast.
- *
- * @param tree a previously loaded tree.
- * @param id the sha being looked for
- * @return the tree entry; NULL if not found
- */
-GIT_EXTERN(const git_tree_entry *) git_tree_entry_byid(
- const git_tree *tree, const git_oid *id);
-
-/**
- * Retrieve a tree entry contained in a tree or in any of its subtrees,
- * given its relative path.
- *
- * Unlike the other lookup functions, the returned tree entry is owned by
- * the user and must be freed explicitly with `git_tree_entry_free()`.
- *
- * @param out Pointer where to store the tree entry
- * @param root Previously loaded tree which is the root of the relative path
- * @param path Path to the contained entry
- * @return 0 on success; GIT_ENOTFOUND if the path does not exist
- */
-GIT_EXTERN(int) git_tree_entry_bypath(
- git_tree_entry **out,
- const git_tree *root,
- const char *path);
-
-/**
- * Duplicate a tree entry
- *
- * Create a copy of a tree entry. The returned copy is owned by the user,
- * and must be freed explicitly with `git_tree_entry_free()`.
- *
- * @param dest pointer where to store the copy
- * @param source tree entry to duplicate
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source);
-
-/**
- * Free a user-owned tree entry
- *
- * IMPORTANT: This function is only needed for tree entries owned by the
- * user, such as the ones returned by `git_tree_entry_dup()` or
- * `git_tree_entry_bypath()`.
- *
- * @param entry The entry to free
- */
-GIT_EXTERN(void) git_tree_entry_free(git_tree_entry *entry);
-
-/**
- * Get the filename of a tree entry
- *
- * @param entry a tree entry
- * @return the name of the file
- */
-GIT_EXTERN(const char *) git_tree_entry_name(const git_tree_entry *entry);
-
-/**
- * Get the id of the object pointed by the entry
- *
- * @param entry a tree entry
- * @return the oid of the object
- */
-GIT_EXTERN(const git_oid *) git_tree_entry_id(const git_tree_entry *entry);
-
-/**
- * Get the type of the object pointed by the entry
- *
- * @param entry a tree entry
- * @return the type of the pointed object
- */
-GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry);
-
-/**
- * Get the UNIX file attributes of a tree entry
- *
- * @param entry a tree entry
- * @return filemode as an integer
- */
-GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry);
-
-/**
- * Get the raw UNIX file attributes of a tree entry
- *
- * This function does not perform any normalization and is only useful
- * if you need to be able to recreate the original tree object.
- *
- * @param entry a tree entry
- * @return filemode as an integer
- */
-
-GIT_EXTERN(git_filemode_t) git_tree_entry_filemode_raw(const git_tree_entry *entry);
-/**
- * Compare two tree entries
- *
- * @param e1 first tree entry
- * @param e2 second tree entry
- * @return <0 if e1 is before e2, 0 if e1 == e2, >0 if e1 is after e2
- */
-GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2);
-
-/**
- * Convert a tree entry to the git_object it points to.
- *
- * You must call `git_object_free()` on the object when you are done with it.
- *
- * @param object_out pointer to the converted object
- * @param repo repository where to lookup the pointed object
- * @param entry a tree entry
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tree_entry_to_object(
- git_object **object_out,
- git_repository *repo,
- const git_tree_entry *entry);
-
-/**
- * Create a new tree builder.
- *
- * The tree builder can be used to create or modify trees in memory and
- * write them as tree objects to the database.
- *
- * If the `source` parameter is not NULL, the tree builder will be
- * initialized with the entries of the given tree.
- *
- * If the `source` parameter is NULL, the tree builder will start with no
- * entries and will have to be filled manually.
- *
- * @param out Pointer where to store the tree builder
- * @param repo Repository in which to store the object
- * @param source Source tree to initialize the builder (optional)
- * @return 0 on success; error code otherwise
- */
-GIT_EXTERN(int) git_treebuilder_new(
- git_treebuilder **out, git_repository *repo, const git_tree *source);
-
-/**
- * Clear all the entires in the builder
- *
- * @param bld Builder to clear
- */
-GIT_EXTERN(void) git_treebuilder_clear(git_treebuilder *bld);
-
-/**
- * Get the number of entries listed in a treebuilder
- *
- * @param bld a previously loaded treebuilder.
- * @return the number of entries in the treebuilder
- */
-GIT_EXTERN(unsigned int) git_treebuilder_entrycount(git_treebuilder *bld);
-
-/**
- * Free a tree builder
- *
- * This will clear all the entries and free to builder.
- * Failing to free the builder after you're done using it
- * will result in a memory leak
- *
- * @param bld Builder to free
- */
-GIT_EXTERN(void) git_treebuilder_free(git_treebuilder *bld);
-
-/**
- * Get an entry from the builder from its filename
- *
- * The returned entry is owned by the builder and should
- * not be freed manually.
- *
- * @param bld Tree builder
- * @param filename Name of the entry
- * @return pointer to the entry; NULL if not found
- */
-GIT_EXTERN(const git_tree_entry *) git_treebuilder_get(
- git_treebuilder *bld, const char *filename);
-
-/**
- * Add or update an entry to the builder
- *
- * Insert a new entry for `filename` in the builder with the
- * given attributes.
- *
- * If an entry named `filename` already exists, its attributes
- * will be updated with the given ones.
- *
- * The optional pointer `out` can be used to retrieve a pointer to the
- * newly created/updated entry. Pass NULL if you do not need it. The
- * pointer may not be valid past the next operation in this
- * builder. Duplicate the entry if you want to keep it.
- *
- * No attempt is being made to ensure that the provided oid points
- * to an existing git object in the object database, nor that the
- * attributes make sense regarding the type of the pointed at object.
- *
- * @param out Pointer to store the entry (optional)
- * @param bld Tree builder
- * @param filename Filename of the entry
- * @param id SHA1 oid of the entry
- * @param filemode Folder attributes of the entry. This parameter must
- * be valued with one of the following entries: 0040000, 0100644,
- * 0100755, 0120000 or 0160000.
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_treebuilder_insert(
- const git_tree_entry **out,
- git_treebuilder *bld,
- const char *filename,
- const git_oid *id,
- git_filemode_t filemode);
-
-/**
- * Remove an entry from the builder by its filename
- *
- * @param bld Tree builder
- * @param filename Filename of the entry to remove
- */
-GIT_EXTERN(int) git_treebuilder_remove(
- git_treebuilder *bld, const char *filename);
-
-/**
- * Callback for git_treebuilder_filter
- *
- * The return value is treated as a boolean, with zero indicating that the
- * entry should be left alone and any non-zero value meaning that the
- * entry should be removed from the treebuilder list (i.e. filtered out).
- */
-typedef int (*git_treebuilder_filter_cb)(
- const git_tree_entry *entry, void *payload);
-
-/**
- * Selectively remove entries in the tree
- *
- * The `filter` callback will be called for each entry in the tree with a
- * pointer to the entry and the provided `payload`; if the callback returns
- * non-zero, the entry will be filtered (removed from the builder).
- *
- * @param bld Tree builder
- * @param filter Callback to filter entries
- * @param payload Extra data to pass to filter callback
- */
-GIT_EXTERN(void) git_treebuilder_filter(
- git_treebuilder *bld,
- git_treebuilder_filter_cb filter,
- void *payload);
-
-/**
- * Write the contents of the tree builder as a tree object
- *
- * The tree builder will be written to the given `repo`, and its
- * identifying SHA1 hash will be stored in the `id` pointer.
- *
- * @param id Pointer to store the OID of the newly written tree
- * @param bld Tree builder to write
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_treebuilder_write(
- git_oid *id, git_treebuilder *bld);
-
-
-/** Callback for the tree traversal method */
-typedef int (*git_treewalk_cb)(
- const char *root, const git_tree_entry *entry, void *payload);
-
-/** Tree traversal modes */
-typedef enum {
- GIT_TREEWALK_PRE = 0, /* Pre-order */
- GIT_TREEWALK_POST = 1, /* Post-order */
-} git_treewalk_mode;
-
-/**
- * Traverse the entries in a tree and its subtrees in post or pre order.
- *
- * The entries will be traversed in the specified order, children subtrees
- * will be automatically loaded as required, and the `callback` will be
- * called once per entry with the current (relative) root for the entry and
- * the entry data itself.
- *
- * If the callback returns a positive value, the passed entry will be
- * skipped on the traversal (in pre mode). A negative value stops the walk.
- *
- * @param tree The tree to walk
- * @param mode Traversal mode (pre or post-order)
- * @param callback Function to call on each tree entry
- * @param payload Opaque pointer to be passed on each callback
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_tree_walk(
- const git_tree *tree,
- git_treewalk_mode mode,
- git_treewalk_cb callback,
- void *payload);
-
-/**
- * Create an in-memory copy of a tree. The copy must be explicitly
- * free'd or it will leak.
- *
- * @param out Pointer to store the copy of the tree
- * @param source Original tree to copy
- */
-GIT_EXTERN(int) git_tree_dup(git_tree **out, git_tree *source);
-
-/**
- * The kind of update to perform
- */
-typedef enum {
- /** Update or insert an entry at the specified path */
- GIT_TREE_UPDATE_UPSERT,
- /** Remove an entry from the specified path */
- GIT_TREE_UPDATE_REMOVE,
-} git_tree_update_t;
-
-/**
- * An action to perform during the update of a tree
- */
-typedef struct {
- /** Update action. If it's an removal, only the path is looked at */
- git_tree_update_t action;
- /** The entry's id */
- git_oid id;
- /** The filemode/kind of object */
- git_filemode_t filemode;
- /** The full path from the root tree */
- const char *path;
-} git_tree_update;
-
-/**
- * Create a tree based on another one with the specified modifications
- *
- * Given the `baseline` perform the changes described in the list of
- * `updates` and create a new tree.
- *
- * This function is optimized for common file/directory addition, removal and
- * replacement in trees. It is much more efficient than reading the tree into a
- * `git_index` and modifying that, but in exchange it is not as flexible.
- *
- * Deleting and adding the same entry is undefined behaviour, changing
- * a tree to a blob or viceversa is not supported.
- *
- * @param out id of the new tree
- * @param repo the repository in which to create the tree, must be the
- * same as for `baseline`
- * @param baseline the tree to base these changes on
- * @param nupdates the number of elements in the update list
- * @param updates the list of updates to perform
- */
-GIT_EXTERN(int) git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseline, size_t nupdates, const git_tree_update *updates);
-
-/** @} */
-
-GIT_END_DECL
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_types_h__
-#define INCLUDE_git_types_h__
-
-#include "common.h"
-
-/**
- * @file git2/types.h
- * @brief libgit2 base & compatibility types
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Cross-platform compatibility types for off_t / time_t
- *
- * NOTE: This needs to be in a public header so that both the library
- * implementation and client applications both agree on the same types.
- * Otherwise we get undefined behavior.
- *
- * Use the "best" types that each platform provides. Currently we truncate
- * these intermediate representations for compatibility with the git ABI, but
- * if and when it changes to support 64 bit types, our code will naturally
- * adapt.
- * NOTE: These types should match those that are returned by our internal
- * stat() functions, for all platforms.
- */
-#include <sys/types.h>
-#ifdef __amigaos4__
-#include <stdint.h>
-#endif
-
-#if defined(_MSC_VER)
-
-typedef __int64 git_off_t;
-typedef __time64_t git_time_t;
-
-#elif defined(__MINGW32__)
-
-typedef off64_t git_off_t;
-typedef __time64_t git_time_t;
-
-#elif defined(__HAIKU__)
-
-typedef __haiku_std_int64 git_off_t;
-typedef __haiku_std_int64 git_time_t;
-
-#else /* POSIX */
-
-/*
- * Note: Can't use off_t since if a client program includes <sys/types.h>
- * before us (directly or indirectly), they'll get 32 bit off_t in their client
- * app, even though /we/ define _FILE_OFFSET_BITS=64.
- */
-typedef int64_t git_off_t;
-typedef int64_t git_time_t;
-
-#endif
-
-/** Basic type (loose or packed) of any Git object. */
-typedef enum {
- GIT_OBJ_ANY = -2, /**< Object can be any of the following */
- GIT_OBJ_BAD = -1, /**< Object is invalid. */
- GIT_OBJ__EXT1 = 0, /**< Reserved for future use. */
- GIT_OBJ_COMMIT = 1, /**< A commit object. */
- GIT_OBJ_TREE = 2, /**< A tree (directory listing) object. */
- GIT_OBJ_BLOB = 3, /**< A file revision object. */
- GIT_OBJ_TAG = 4, /**< An annotated tag object. */
- GIT_OBJ__EXT2 = 5, /**< Reserved for future use. */
- GIT_OBJ_OFS_DELTA = 6, /**< A delta, base is given by an offset. */
- GIT_OBJ_REF_DELTA = 7, /**< A delta, base is given by object id. */
-} git_otype;
-
-/** An open object database handle. */
-typedef struct git_odb git_odb;
-
-/** A custom backend in an ODB */
-typedef struct git_odb_backend git_odb_backend;
-
-/** An object read from the ODB */
-typedef struct git_odb_object git_odb_object;
-
-/** A stream to read/write from the ODB */
-typedef struct git_odb_stream git_odb_stream;
-
-/** A stream to write a packfile to the ODB */
-typedef struct git_odb_writepack git_odb_writepack;
-
-/** An open refs database handle. */
-typedef struct git_refdb git_refdb;
-
-/** A custom backend for refs */
-typedef struct git_refdb_backend git_refdb_backend;
-
-/**
- * Representation of an existing git repository,
- * including all its object contents
- */
-typedef struct git_repository git_repository;
-
-/** Representation of a generic object in a repository */
-typedef struct git_object git_object;
-
-/** Representation of an in-progress walk through the commits in a repo */
-typedef struct git_revwalk git_revwalk;
-
-/** Parsed representation of a tag object. */
-typedef struct git_tag git_tag;
-
-/** In-memory representation of a blob object. */
-typedef struct git_blob git_blob;
-
-/** Parsed representation of a commit object. */
-typedef struct git_commit git_commit;
-
-/** Representation of each one of the entries in a tree object. */
-typedef struct git_tree_entry git_tree_entry;
-
-/** Representation of a tree object. */
-typedef struct git_tree git_tree;
-
-/** Constructor for in-memory trees */
-typedef struct git_treebuilder git_treebuilder;
-
-/** Memory representation of an index file. */
-typedef struct git_index git_index;
-
-/** An iterator for conflicts in the index. */
-typedef struct git_index_conflict_iterator git_index_conflict_iterator;
-
-/** Memory representation of a set of config files */
-typedef struct git_config git_config;
-
-/** Interface to access a configuration file */
-typedef struct git_config_backend git_config_backend;
-
-/** Representation of a reference log entry */
-typedef struct git_reflog_entry git_reflog_entry;
-
-/** Representation of a reference log */
-typedef struct git_reflog git_reflog;
-
-/** Representation of a git note */
-typedef struct git_note git_note;
-
-/** Representation of a git packbuilder */
-typedef struct git_packbuilder git_packbuilder;
-
-/** Time in a signature */
-typedef struct git_time {
- git_time_t time; /**< time in seconds from epoch */
- int offset; /**< timezone offset, in minutes */
-} git_time;
-
-/** An action signature (e.g. for committers, taggers, etc) */
-typedef struct git_signature {
- char *name; /**< full name of the author */
- char *email; /**< email of the author */
- git_time when; /**< time when the action happened */
-} git_signature;
-
-/** In-memory representation of a reference. */
-typedef struct git_reference git_reference;
-
-/** Iterator for references */
-typedef struct git_reference_iterator git_reference_iterator;
-
-/** Transactional interface to references */
-typedef struct git_transaction git_transaction;
-
-/** Annotated commits, the input to merge and rebase. */
-typedef struct git_annotated_commit git_annotated_commit;
-
-/** Merge result */
-typedef struct git_merge_result git_merge_result;
-
-/** Representation of a status collection */
-typedef struct git_status_list git_status_list;
-
-/** Representation of a rebase */
-typedef struct git_rebase git_rebase;
-
-/** Basic type of any Git reference. */
-typedef enum {
- GIT_REF_INVALID = 0, /**< Invalid reference */
- GIT_REF_OID = 1, /**< A reference which points at an object id */
- GIT_REF_SYMBOLIC = 2, /**< A reference which points at another reference */
- GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC,
-} git_ref_t;
-
-/** Basic type of any Git branch. */
-typedef enum {
- GIT_BRANCH_LOCAL = 1,
- GIT_BRANCH_REMOTE = 2,
- GIT_BRANCH_ALL = GIT_BRANCH_LOCAL|GIT_BRANCH_REMOTE,
-} git_branch_t;
-
-/** Valid modes for index and tree entries. */
-typedef enum {
- GIT_FILEMODE_UNREADABLE = 0000000,
- GIT_FILEMODE_TREE = 0040000,
- GIT_FILEMODE_BLOB = 0100644,
- GIT_FILEMODE_BLOB_EXECUTABLE = 0100755,
- GIT_FILEMODE_LINK = 0120000,
- GIT_FILEMODE_COMMIT = 0160000,
-} git_filemode_t;
-
-/*
- * A refspec specifies the mapping between remote and local reference
- * names when fetch or pushing.
- */
-typedef struct git_refspec git_refspec;
-
-/**
- * Git's idea of a remote repository. A remote can be anonymous (in
- * which case it does not have backing configuration entires).
- */
-typedef struct git_remote git_remote;
-
-/**
- * Interface which represents a transport to communicate with a
- * remote.
- */
-typedef struct git_transport git_transport;
-
-/**
- * Preparation for a push operation. Can be used to configure what to
- * push and the level of parallelism of the packfile builder.
- */
-typedef struct git_push git_push;
-
-/* documentation in the definition */
-typedef struct git_remote_head git_remote_head;
-typedef struct git_remote_callbacks git_remote_callbacks;
-
-/**
- * This is passed as the first argument to the callback to allow the
- * user to see the progress.
- *
- * - total_objects: number of objects in the packfile being downloaded
- * - indexed_objects: received objects that have been hashed
- * - received_objects: objects which have been downloaded
- * - local_objects: locally-available objects that have been injected
- * in order to fix a thin pack.
- * - received-bytes: size of the packfile received up to now
- */
-typedef struct git_transfer_progress {
- unsigned int total_objects;
- unsigned int indexed_objects;
- unsigned int received_objects;
- unsigned int local_objects;
- unsigned int total_deltas;
- unsigned int indexed_deltas;
- size_t received_bytes;
-} git_transfer_progress;
-
-/**
- * Type for progress callbacks during indexing. Return a value less than zero
- * to cancel the transfer.
- *
- * @param stats Structure containing information about the state of the transfer
- * @param payload Payload provided by caller
- */
-typedef int (*git_transfer_progress_cb)(const git_transfer_progress *stats, void *payload);
-
-/**
- * Type for messages delivered by the transport. Return a negative value
- * to cancel the network operation.
- *
- * @param str The message from the transport
- * @param len The length of the message
- * @param payload Payload provided by the caller
- */
-typedef int (*git_transport_message_cb)(const char *str, int len, void *payload);
-
-
-/**
- * Type of host certificate structure that is passed to the check callback
- */
-typedef enum git_cert_t {
- /**
- * No information about the certificate is available. This may
- * happen when using curl.
- */
- GIT_CERT_NONE,
- /**
- * The `data` argument to the callback will be a pointer to
- * the DER-encoded data.
- */
- GIT_CERT_X509,
- /**
- * The `data` argument to the callback will be a pointer to a
- * `git_cert_hostkey` structure.
- */
- GIT_CERT_HOSTKEY_LIBSSH2,
- /**
- * The `data` argument to the callback will be a pointer to a
- * `git_strarray` with `name:content` strings containing
- * information about the certificate. This is used when using
- * curl.
- */
- GIT_CERT_STRARRAY,
-} git_cert_t;
-
-/**
- * Parent type for `git_cert_hostkey` and `git_cert_x509`.
- */
-typedef struct {
- /**
- * Type of certificate. A `GIT_CERT_` value.
- */
- git_cert_t cert_type;
-} git_cert;
-
-/**
- * Callback for the user's custom certificate checks.
- *
- * @param cert The host certificate
- * @param valid Whether the libgit2 checks (OpenSSL or WinHTTP) think
- * this certificate is valid
- * @param host Hostname of the host libgit2 connected to
- * @param payload Payload provided by the caller
- */
-typedef int (*git_transport_certificate_check_cb)(git_cert *cert, int valid, const char *host, void *payload);
-
-/**
- * Opaque structure representing a submodule.
- */
-typedef struct git_submodule git_submodule;
-
-/**
- * Submodule update values
- *
- * These values represent settings for the `submodule.$name.update`
- * configuration value which says how to handle `git submodule update` for
- * this submodule. The value is usually set in the ".gitmodules" file and
- * copied to ".git/config" when the submodule is initialized.
- *
- * You can override this setting on a per-submodule basis with
- * `git_submodule_set_update()` and write the changed value to disk using
- * `git_submodule_save()`. If you have overwritten the value, you can
- * revert it by passing `GIT_SUBMODULE_UPDATE_RESET` to the set function.
- *
- * The values are:
- *
- * - GIT_SUBMODULE_UPDATE_CHECKOUT: the default; when a submodule is
- * updated, checkout the new detached HEAD to the submodule directory.
- * - GIT_SUBMODULE_UPDATE_REBASE: update by rebasing the current checked
- * out branch onto the commit from the superproject.
- * - GIT_SUBMODULE_UPDATE_MERGE: update by merging the commit in the
- * superproject into the current checkout out branch of the submodule.
- * - GIT_SUBMODULE_UPDATE_NONE: do not update this submodule even when
- * the commit in the superproject is updated.
- * - GIT_SUBMODULE_UPDATE_DEFAULT: not used except as static initializer
- * when we don't want any particular update rule to be specified.
- */
-typedef enum {
- GIT_SUBMODULE_UPDATE_CHECKOUT = 1,
- GIT_SUBMODULE_UPDATE_REBASE = 2,
- GIT_SUBMODULE_UPDATE_MERGE = 3,
- GIT_SUBMODULE_UPDATE_NONE = 4,
-
- GIT_SUBMODULE_UPDATE_DEFAULT = 0
-} git_submodule_update_t;
-
-/**
- * Submodule ignore values
- *
- * These values represent settings for the `submodule.$name.ignore`
- * configuration value which says how deeply to look at the working
- * directory when getting submodule status.
- *
- * You can override this value in memory on a per-submodule basis with
- * `git_submodule_set_ignore()` and can write the changed value to disk
- * with `git_submodule_save()`. If you have overwritten the value, you
- * can revert to the on disk value by using `GIT_SUBMODULE_IGNORE_RESET`.
- *
- * The values are:
- *
- * - GIT_SUBMODULE_IGNORE_UNSPECIFIED: use the submodule's configuration
- * - GIT_SUBMODULE_IGNORE_NONE: don't ignore any change - i.e. even an
- * untracked file, will mark the submodule as dirty. Ignored files are
- * still ignored, of course.
- * - GIT_SUBMODULE_IGNORE_UNTRACKED: ignore untracked files; only changes
- * to tracked files, or the index or the HEAD commit will matter.
- * - GIT_SUBMODULE_IGNORE_DIRTY: ignore changes in the working directory,
- * only considering changes if the HEAD of submodule has moved from the
- * value in the superproject.
- * - GIT_SUBMODULE_IGNORE_ALL: never check if the submodule is dirty
- * - GIT_SUBMODULE_IGNORE_DEFAULT: not used except as static initializer
- * when we don't want any particular ignore rule to be specified.
- */
-typedef enum {
- GIT_SUBMODULE_IGNORE_UNSPECIFIED = -1, /**< use the submodule's configuration */
-
- GIT_SUBMODULE_IGNORE_NONE = 1, /**< any change or untracked == dirty */
- GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */
- GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */
- GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */
-} git_submodule_ignore_t;
-
-/**
- * Options for submodule recurse.
- *
- * Represent the value of `submodule.$name.fetchRecurseSubmodules`
- *
- * * GIT_SUBMODULE_RECURSE_NO - do no recurse into submodules
- * * GIT_SUBMODULE_RECURSE_YES - recurse into submodules
- * * GIT_SUBMODULE_RECURSE_ONDEMAND - recurse into submodules only when
- * commit not already in local clone
- */
-typedef enum {
- GIT_SUBMODULE_RECURSE_NO = 0,
- GIT_SUBMODULE_RECURSE_YES = 1,
- GIT_SUBMODULE_RECURSE_ONDEMAND = 2,
-} git_submodule_recurse_t;
-
-/** A type to write in a streaming fashion, for example, for filters. */
-typedef struct git_writestream git_writestream;
-
-struct git_writestream {
- int (*write)(git_writestream *stream, const char *buffer, size_t len);
- int (*close)(git_writestream *stream);
- void (*free)(git_writestream *stream);
-};
-
-/** @} */
-GIT_END_DECL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_version_h__
-#define INCLUDE_git_version_h__
-
-#define LIBGIT2_VERSION "0.25.0"
-#define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 25
-#define LIBGIT2_VER_REVISION 0
-#define LIBGIT2_VER_PATCH 0
-
-#define LIBGIT2_SOVERSION 25
-
-#endif
+++ /dev/null
-prefix=@PKGCONFIG_PREFIX@
-libdir=@PKGCONFIG_LIBDIR@
-includedir=@PKGCONFIG_INCLUDEDIR@
-
-Name: libgit2
-Description: The git library, take 2
-Version: @LIBGIT2_VERSION_STRING@
-
-Libs: -L"${libdir}" -lgit2
-Libs.private: @LIBGIT2_PC_LIBS@
-Requires.private: @LIBGIT2_PC_REQUIRES@
-
-Cflags: -I${includedir}
+++ /dev/null
-{
- ignore-zlib-errors-cond
- Memcheck:Cond
- obj:*libz.so*
-}
-
-{
- ignore-giterr-set-leak
- Memcheck:Leak
- ...
- fun:giterr_set
-}
-
-{
- ignore-git-global-state-leak
- Memcheck:Leak
- ...
- fun:git__global_state
-}
-
-{
- ignore-openssl-ssl-leak
- Memcheck:Leak
- ...
- obj:*libssl.so*
- ...
-}
-
-{
- ignore-openssl-crypto-leak
- Memcheck:Leak
- ...
- obj:*libcrypto.so*
- ...
-}
-
-{
- ignore-openssl-crypto-cond
- Memcheck:Cond
- obj:*libcrypto.so*
- ...
-}
-
-{
- ignore-glibc-getaddrinfo-cache
- Memcheck:Leak
- ...
- fun:__check_pf
-}
+++ /dev/null
-#!/bin/sh
-set -e
-cd `dirname "$0"`/..
-if [ "$ARCH" = "i686" ]; then
- f=i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z
- if ! [ -e $f ]; then
- curl -LsSO http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-win32/sjlj/$f
- fi
- 7z x $f > /dev/null
- mv mingw32 /MinGW
-else
- f=x86_64-4.9.2-release-win32-seh-rt_v3-rev1.7z
- if ! [ -e $f ]; then
- curl -LsSO http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/$f
- fi
- 7z x $f > /dev/null
- mv mingw64 /MinGW
-fi
-cd build
-cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON .. -G"$GENERATOR"
-cmake --build . --config RelWithDebInfo
+++ /dev/null
-#!/bin/sh
-
-set -x
-
-if [ -n "$COVERITY" ];
-then
- ./script/coverity.sh;
- exit $?;
-fi
-
-if [ "$TRAVIS_OS_NAME" = "osx" ]; then
- export PKG_CONFIG_PATH=$(ls -d /usr/local/Cellar/{curl,zlib}/*/lib/pkgconfig | paste -s -d':' -)
-fi
-
-# Should we ask Travis to cache this file?
-curl -L https://github.com/ethomson/poxyproxy/releases/download/v0.1.0/poxyproxy-0.1.0.jar >poxyproxy.jar || exit $?
-# Run this early so we know it's ready by the time we need it
-java -jar poxyproxy.jar -d --port 8080 --credentials foo:bar &
-
-mkdir _build
-cd _build
-# shellcheck disable=SC2086
-cmake .. -DBUILD_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS || exit $?
-make -j2 install || exit $?
-
-# If this platform doesn't support test execution, bail out now
-if [ -n "$SKIP_TESTS" ];
-then
- exit $?;
-fi
-
-# Create a test repo which we can use for the online::push tests
-mkdir "$HOME"/_temp
-git init --bare "$HOME"/_temp/test.git
-git daemon --listen=localhost --export-all --enable=receive-pack --base-path="$HOME"/_temp "$HOME"/_temp 2>/dev/null &
-export GITTEST_REMOTE_URL="git://localhost/test.git"
-
-# Run the test suite
-ctest -V -R libgit2_clar || exit $?
-
-# Now that we've tested the raw git protocol, let's set up ssh to we
-# can do the push tests over it
-
-killall git-daemon
-
-if [ "$TRAVIS_OS_NAME" = "osx" ]; then
- echo 'PasswordAuthentication yes' | sudo tee -a /etc/sshd_config
-fi
-
-ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
-cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
-ssh-keyscan -t rsa localhost >>~/.ssh/known_hosts
-
-# Get the fingerprint for localhost and remove the colons so we can parse it as
-# a hex number. The Mac version is newer so it has a different output format.
-if [ "$TRAVIS_OS_NAME" = "osx" ]; then
- export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -E md5 -F localhost -l | tail -n 1 | cut -d ' ' -f 3 | cut -d : -f2- | tr -d :)
-else
- export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -F localhost -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
-fi
-
-export GITTEST_REMOTE_URL="ssh://localhost/$HOME/_temp/test.git"
-export GITTEST_REMOTE_USER=$USER
-export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa"
-export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub"
-export GITTEST_REMOTE_SSH_PASSPHRASE=""
-
-
-if [ -e ./libgit2_clar ]; then
- ./libgit2_clar -sonline::push -sonline::clone::ssh_cert &&
- ./libgit2_clar -sonline::clone::ssh_with_paths || exit $?
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
- ./libgit2_clar -sonline::clone::cred_callback || exit $?
- fi
-
- # Use the proxy we started at the beginning
- export GITTEST_REMOTE_PROXY_URL="http://foo:bar@localhost:8080/"
- ./libgit2_clar -sonline::clone::proxy_credentials_in_url || exit $?
- export GITTEST_REMOTE_PROXY_URL="http://localhost:8080/"
- export GITTEST_REMOTE_PROXY_USER="foo"
- export GITTEST_REMOTE_PROXY_PASS="bar"
- ./libgit2_clar -sonline::clone::proxy_credentials_request || exit $?
-
-fi
-
-export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
-export GITTEST_REMOTE_USER="libgit2test"
-ctest -V -R libgit2_clar-cred_callback
+++ /dev/null
-#!/bin/bash
-set -e
-
-# Only run this on our branches
-echo "Branch: $TRAVIS_BRANCH | Pull request: $TRAVIS_PULL_REQUEST | Slug: $TRAVIS_REPO_SLUG"
-if [ "$TRAVIS_BRANCH" != "master" -o "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_REPO_SLUG" != "libgit2/libgit2" ];
-then
- echo "Only analyzing the 'master' brach of the main repository."
- exit 0
-fi
-
-# Environment check
-[ -z "$COVERITY_TOKEN" ] && echo "Need to set a coverity token" && exit 1
-
-case $(uname -m) in
- i?86) BITS=32 ;;
- amd64|x86_64) BITS=64 ;;
-esac
-SCAN_TOOL=https://scan.coverity.com/download/cxx/linux${BITS}
-TOOL_BASE=$(pwd)/_coverity-scan
-
-# Install coverity tools
-if [ ! -d "$TOOL_BASE" ]; then
- echo "Downloading coverity..."
- mkdir -p "$TOOL_BASE"
- pushd "$TOOL_BASE"
- wget -O coverity_tool.tgz $SCAN_TOOL \
- --post-data "project=libgit2&token=$COVERITY_TOKEN"
- tar xzf coverity_tool.tgz
- popd
- TOOL_DIR=$(find "$TOOL_BASE" -type d -name 'cov-analysis*')
- ln -s "$TOOL_DIR" "$TOOL_BASE"/cov-analysis
-fi
-
-cp script/user_nodefs.h "$TOOL_BASE"/cov-analysis/config/user_nodefs.h
-
-COV_BUILD="$TOOL_BASE/cov-analysis/bin/cov-build"
-
-# Configure and build
-rm -rf _build
-mkdir _build
-cd _build
-cmake .. -DTHREADSAFE=ON
-COVERITY_UNSUPPORTED=1 \
- $COV_BUILD --dir cov-int \
- cmake --build .
-
-# Upload results
-tar czf libgit2.tgz cov-int
-SHA=$(git rev-parse --short HEAD)
-
-HTML="$(curl \
- --silent \
- --write-out "\n%{http_code}" \
- --form token="$COVERITY_TOKEN" \
- --form email=bs@github.com \
- --form file=@libgit2.tgz \
- --form version="$SHA" \
- --form description="Travis build" \
- https://scan.coverity.com/builds?project=libgit2)"
-# Body is everything up to the last line
-BODY="$(echo "$HTML" | head -n-1)"
-# Status code is the last line
-STATUS_CODE="$(echo "$HTML" | tail -n1)"
-
-echo "${BODY}"
-
-if [ "${STATUS_CODE}" != "201" ]; then
- echo "Received error code ${STATUS_CODE} from Coverity"
- exit 1
-fi
+++ /dev/null
-#!/bin/sh
-
-set -x
-
-brew update
-brew install homebrew/dupes/zlib
-brew install curl
-brew install libssh2
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-void *realloc(void *ptr, size_t size);
-void *memmove(void *dest, const void *src, size_t n);
-size_t strlen(const char *s);
-
-typedef struct va_list_str *va_list;
-
-typedef struct git_vector {
- void **contents;
- size_t length;
-} git_vector;
-
-typedef struct git_buf {
- char *ptr;
- size_t asize, size;
-} git_buf;
-
-int git_vector_insert(git_vector *v, void *element)
-{
- if (!v)
- __coverity_panic__();
-
- v->contents = realloc(v->contents, ++v->length);
- if (!v->contents)
- __coverity_panic__();
- v->contents[v->length] = element;
-
- return 0;
-}
-
-int git_buf_len(const struct git_buf *buf)
-{
- return strlen(buf->ptr);
-}
-
-int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
-{
- char ch, *s;
- size_t len;
-
- __coverity_string_null_sink__(format);
- __coverity_string_size_sink__(format);
-
- ch = *format;
- ch = *(char *)ap;
-
- buf->ptr = __coverity_alloc__(len);
- __coverity_writeall__(buf->ptr);
- buf->size = len;
-
- return 0;
-}
-
-int git_buf_put(git_buf *buf, const char *data, size_t len)
-{
- buf->ptr = __coverity_alloc__(buf->size + len + 1);
- memmove(buf->ptr + buf->size, data, len);
- buf->size += len;
- buf->ptr[buf->size + len] = 0;
- return 0;
-}
-
-int git_buf_set(git_buf *buf, const void *data, size_t len)
-{
- buf->ptr = __coverity_alloc__(len + 1);
- memmove(buf->ptr, data, len);
- buf->size = len + 1;
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#nodef GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { __coverity_panic__(); }
-#nodef GITERR_CHECK_ALLOC_BUF(buf) if (buf == NULL || git_buf_oom(buf)) { __coverity_panic__(); }
-
-#nodef GITERR_CHECK_ALLOC_ADD(out, one, two) \
- if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { __coverity_panic__(); }
-
-#nodef GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \
- if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
- GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { __coverity_panic__(); }
-
-#nodef GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \
- if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
- GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
- GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { __coverity_panic__(); }
-
-#nodef GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \
- if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { __coverity_panic__(); }
-
-#nodef GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) { __coverity_panic__(); }
-
-#nodef LOOKS_LIKE_DRIVE_PREFIX(S) (strlen(S) >= 2 && git__isalpha((S)[0]) && (S)[1] == ':')
-
-#nodef git_vector_foreach(v, iter, elem) \
- for ((iter) = 0; (v)->contents != NULL && (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ )
-
-#nodef git_vector_rforeach(v, iter, elem) \
- for ((iter) = (v)->length - 1; (v)->contents != NULL && (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- )
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "annotated_commit.h"
-#include "refs.h"
-#include "cache.h"
-
-#include "git2/commit.h"
-#include "git2/refs.h"
-#include "git2/repository.h"
-#include "git2/annotated_commit.h"
-#include "git2/revparse.h"
-#include "git2/tree.h"
-#include "git2/index.h"
-
-static int annotated_commit_init(
- git_annotated_commit **out,
- git_commit *commit,
- const char *description)
-{
- git_annotated_commit *annotated_commit;
- int error = 0;
-
- assert(out && commit);
-
- *out = NULL;
-
- annotated_commit = git__calloc(1, sizeof(git_annotated_commit));
- GITERR_CHECK_ALLOC(annotated_commit);
-
- annotated_commit->type = GIT_ANNOTATED_COMMIT_REAL;
-
- if ((error = git_commit_dup(&annotated_commit->commit, commit)) < 0)
- goto done;
-
- git_oid_fmt(annotated_commit->id_str, git_commit_id(commit));
- annotated_commit->id_str[GIT_OID_HEXSZ] = '\0';
-
- if (!description)
- description = annotated_commit->id_str;
-
- annotated_commit->description = git__strdup(description);
- GITERR_CHECK_ALLOC(annotated_commit->description);
-
-done:
- if (!error)
- *out = annotated_commit;
-
- return error;
-}
-
-static int annotated_commit_init_from_id(
- git_annotated_commit **out,
- git_repository *repo,
- const git_oid *id,
- const char *description)
-{
- git_commit *commit = NULL;
- int error = 0;
-
- assert(out && repo && id);
-
- *out = NULL;
-
- if ((error = git_commit_lookup(&commit, repo, id)) < 0)
- goto done;
-
- error = annotated_commit_init(out, commit, description);
-
-done:
- git_commit_free(commit);
- return error;
-}
-
-int git_annotated_commit_lookup(
- git_annotated_commit **out,
- git_repository *repo,
- const git_oid *id)
-{
- return annotated_commit_init_from_id(out, repo, id, NULL);
-}
-
-int git_annotated_commit_from_commit(
- git_annotated_commit **out,
- git_commit *commit)
-{
- return annotated_commit_init(out, commit, NULL);
-}
-
-int git_annotated_commit_from_revspec(
- git_annotated_commit **out,
- git_repository *repo,
- const char *revspec)
-{
- git_object *obj, *commit;
- int error;
-
- assert(out && repo && revspec);
-
- if ((error = git_revparse_single(&obj, repo, revspec)) < 0)
- return error;
-
- if ((error = git_object_peel(&commit, obj, GIT_OBJ_COMMIT))) {
- git_object_free(obj);
- return error;
- }
-
- error = annotated_commit_init(out, (git_commit *)commit, revspec);
-
- git_object_free(obj);
- git_object_free(commit);
-
- return error;
-}
-
-int git_annotated_commit_from_ref(
- git_annotated_commit **out,
- git_repository *repo,
- const git_reference *ref)
-{
- git_reference *resolved;
- int error = 0;
-
- assert(out && repo && ref);
-
- *out = NULL;
-
- if ((error = git_reference_resolve(&resolved, ref)) < 0)
- return error;
-
- error = annotated_commit_init_from_id(out,
- repo,
- git_reference_target(resolved),
- git_reference_name(ref));
-
- if (!error) {
- (*out)->ref_name = git__strdup(git_reference_name(ref));
- GITERR_CHECK_ALLOC((*out)->ref_name);
- }
-
- git_reference_free(resolved);
- return error;
-}
-
-int git_annotated_commit_from_head(
- git_annotated_commit **out,
- git_repository *repo)
-{
- git_reference *head;
- int error;
-
- assert(out && repo);
-
- *out = NULL;
-
- if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
- return -1;
-
- error = git_annotated_commit_from_ref(out, repo, head);
-
- git_reference_free(head);
- return error;
-}
-
-int git_annotated_commit_from_fetchhead(
- git_annotated_commit **out,
- git_repository *repo,
- const char *branch_name,
- const char *remote_url,
- const git_oid *id)
-{
- assert(repo && id && branch_name && remote_url);
-
- if (annotated_commit_init_from_id(out, repo, id, branch_name) < 0)
- return -1;
-
- (*out)->ref_name = git__strdup(branch_name);
- GITERR_CHECK_ALLOC((*out)->ref_name);
-
- (*out)->remote_url = git__strdup(remote_url);
- GITERR_CHECK_ALLOC((*out)->remote_url);
-
- return 0;
-}
-
-
-const git_oid *git_annotated_commit_id(
- const git_annotated_commit *annotated_commit)
-{
- assert(annotated_commit);
- return git_commit_id(annotated_commit->commit);
-}
-
-void git_annotated_commit_free(git_annotated_commit *annotated_commit)
-{
- if (annotated_commit == NULL)
- return;
-
- switch (annotated_commit->type) {
- case GIT_ANNOTATED_COMMIT_REAL:
- git_commit_free(annotated_commit->commit);
- git_tree_free(annotated_commit->tree);
- git__free((char *)annotated_commit->description);
- git__free((char *)annotated_commit->ref_name);
- git__free((char *)annotated_commit->remote_url);
- break;
- case GIT_ANNOTATED_COMMIT_VIRTUAL:
- git_index_free(annotated_commit->index);
- git_array_clear(annotated_commit->parents);
- break;
- default:
- abort();
- }
-
- git__free(annotated_commit);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_annotated_commit_h__
-#define INCLUDE_annotated_commit_h__
-
-#include "oidarray.h"
-
-#include "git2/oid.h"
-
-typedef enum {
- GIT_ANNOTATED_COMMIT_REAL = 1,
- GIT_ANNOTATED_COMMIT_VIRTUAL = 2,
-} git_annotated_commit_t;
-
-/**
- * Internal structure for merge inputs. An annotated commit is generally
- * "real" and backed by an actual commit in the repository, but merge will
- * internally create "virtual" commits that are in-memory intermediate
- * commits backed by an index.
- */
-struct git_annotated_commit {
- git_annotated_commit_t type;
-
- /* real commit */
- git_commit *commit;
- git_tree *tree;
-
- /* virtual commit structure */
- git_index *index;
- git_array_oid_t parents;
-
- /* how this commit was looked up */
- const char *description;
-
- const char *ref_name;
- const char *remote_url;
-
- char id_str[GIT_OID_HEXSZ+1];
-};
-
-extern int git_annotated_commit_from_head(git_annotated_commit **out,
- git_repository *repo);
-extern int git_annotated_commit_from_commit(git_annotated_commit **out,
- git_commit *commit);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include <assert.h>
-
-#include "git2/patch.h"
-#include "git2/filter.h"
-#include "array.h"
-#include "patch.h"
-#include "fileops.h"
-#include "apply.h"
-#include "delta.h"
-#include "zstream.h"
-
-#define apply_err(...) \
- ( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
-
-typedef struct {
- /* The lines that we allocate ourself are allocated out of the pool.
- * (Lines may have been allocated out of the diff.)
- */
- git_pool pool;
- git_vector lines;
-} patch_image;
-
-static void patch_line_init(
- git_diff_line *out,
- const char *in,
- size_t in_len,
- size_t in_offset)
-{
- out->content = in;
- out->content_len = in_len;
- out->content_offset = in_offset;
-}
-
-#define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
-
-static int patch_image_init_fromstr(
- patch_image *out, const char *in, size_t in_len)
-{
- git_diff_line *line;
- const char *start, *end;
-
- memset(out, 0x0, sizeof(patch_image));
-
- git_pool_init(&out->pool, sizeof(git_diff_line));
-
- for (start = in; start < in + in_len; start = end) {
- end = memchr(start, '\n', in_len);
-
- if (end == NULL)
- end = in + in_len;
-
- else if (end < in + in_len)
- end++;
-
- line = git_pool_mallocz(&out->pool, 1);
- GITERR_CHECK_ALLOC(line);
-
- if (git_vector_insert(&out->lines, line) < 0)
- return -1;
-
- patch_line_init(line, start, (end - start), (start - in));
- }
-
- return 0;
-}
-
-static void patch_image_free(patch_image *image)
-{
- if (image == NULL)
- return;
-
- git_pool_clear(&image->pool);
- git_vector_free(&image->lines);
-}
-
-static bool match_hunk(
- patch_image *image,
- patch_image *preimage,
- size_t linenum)
-{
- bool match = 0;
- size_t i;
-
- /* Ensure this hunk is within the image boundaries. */
- if (git_vector_length(&preimage->lines) + linenum >
- git_vector_length(&image->lines))
- return 0;
-
- match = 1;
-
- /* Check exact match. */
- for (i = 0; i < git_vector_length(&preimage->lines); i++) {
- git_diff_line *preimage_line = git_vector_get(&preimage->lines, i);
- git_diff_line *image_line = git_vector_get(&image->lines, linenum + i);
-
- if (preimage_line->content_len != image_line->content_len ||
- memcmp(preimage_line->content, image_line->content, image_line->content_len) != 0) {
- match = 0;
- break;
- }
- }
-
- return match;
-}
-
-static bool find_hunk_linenum(
- size_t *out,
- patch_image *image,
- patch_image *preimage,
- size_t linenum)
-{
- size_t max = git_vector_length(&image->lines);
- bool match;
-
- if (linenum > max)
- linenum = max;
-
- match = match_hunk(image, preimage, linenum);
-
- *out = linenum;
- return match;
-}
-
-static int update_hunk(
- patch_image *image,
- unsigned int linenum,
- patch_image *preimage,
- patch_image *postimage)
-{
- size_t postlen = git_vector_length(&postimage->lines);
- size_t prelen = git_vector_length(&preimage->lines);
- size_t i;
- int error = 0;
-
- if (postlen > prelen)
- error = git_vector_insert_null(
- &image->lines, linenum, (postlen - prelen));
- else if (prelen > postlen)
- error = git_vector_remove_range(
- &image->lines, linenum, (prelen - postlen));
-
- if (error) {
- giterr_set_oom();
- return -1;
- }
-
- for (i = 0; i < git_vector_length(&postimage->lines); i++) {
- image->lines.contents[linenum + i] =
- git_vector_get(&postimage->lines, i);
- }
-
- return 0;
-}
-
-static int apply_hunk(
- patch_image *image,
- git_patch *patch,
- git_patch_hunk *hunk)
-{
- patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT;
- size_t line_num, i;
- int error = 0;
-
- for (i = 0; i < hunk->line_count; i++) {
- size_t linenum = hunk->line_start + i;
- git_diff_line *line = git_array_get(patch->lines, linenum);
-
- if (!line) {
- error = apply_err("Preimage does not contain line %"PRIuZ, linenum);
- goto done;
- }
-
- if (line->origin == GIT_DIFF_LINE_CONTEXT ||
- line->origin == GIT_DIFF_LINE_DELETION) {
- if ((error = git_vector_insert(&preimage.lines, line)) < 0)
- goto done;
- }
-
- if (line->origin == GIT_DIFF_LINE_CONTEXT ||
- line->origin == GIT_DIFF_LINE_ADDITION) {
- if ((error = git_vector_insert(&postimage.lines, line)) < 0)
- goto done;
- }
- }
-
- line_num = hunk->hunk.new_start ? hunk->hunk.new_start - 1 : 0;
-
- if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
- error = apply_err("Hunk at line %d did not apply",
- hunk->hunk.new_start);
- goto done;
- }
-
- error = update_hunk(image, line_num, &preimage, &postimage);
-
-done:
- patch_image_free(&preimage);
- patch_image_free(&postimage);
-
- return error;
-}
-
-static int apply_hunks(
- git_buf *out,
- const char *source,
- size_t source_len,
- git_patch *patch)
-{
- git_patch_hunk *hunk;
- git_diff_line *line;
- patch_image image;
- size_t i;
- int error = 0;
-
- if ((error = patch_image_init_fromstr(&image, source, source_len)) < 0)
- goto done;
-
- git_array_foreach(patch->hunks, i, hunk) {
- if ((error = apply_hunk(&image, patch, hunk)) < 0)
- goto done;
- }
-
- git_vector_foreach(&image.lines, i, line)
- git_buf_put(out, line->content, line->content_len);
-
-done:
- patch_image_free(&image);
-
- return error;
-}
-
-static int apply_binary_delta(
- git_buf *out,
- const char *source,
- size_t source_len,
- git_diff_binary_file *binary_file)
-{
- git_buf inflated = GIT_BUF_INIT;
- int error = 0;
-
- /* no diff means identical contents */
- if (binary_file->datalen == 0)
- return git_buf_put(out, source, source_len);
-
- error = git_zstream_inflatebuf(&inflated,
- binary_file->data, binary_file->datalen);
-
- if (!error && inflated.size != binary_file->inflatedlen) {
- error = apply_err("inflated delta does not match expected length");
- git_buf_free(out);
- }
-
- if (error < 0)
- goto done;
-
- if (binary_file->type == GIT_DIFF_BINARY_DELTA) {
- void *data;
- size_t data_len;
-
- error = git_delta_apply(&data, &data_len, (void *)source, source_len,
- (void *)inflated.ptr, inflated.size);
-
- out->ptr = data;
- out->size = data_len;
- out->asize = data_len;
- }
- else if (binary_file->type == GIT_DIFF_BINARY_LITERAL) {
- git_buf_swap(out, &inflated);
- }
- else {
- error = apply_err("unknown binary delta type");
- goto done;
- }
-
-done:
- git_buf_free(&inflated);
- return error;
-}
-
-static int apply_binary(
- git_buf *out,
- const char *source,
- size_t source_len,
- git_patch *patch)
-{
- git_buf reverse = GIT_BUF_INIT;
- int error = 0;
-
- if (!patch->binary.contains_data) {
- error = apply_err("patch does not contain binary data");
- goto done;
- }
-
- if (!patch->binary.old_file.datalen && !patch->binary.new_file.datalen)
- goto done;
-
- /* first, apply the new_file delta to the given source */
- if ((error = apply_binary_delta(out, source, source_len,
- &patch->binary.new_file)) < 0)
- goto done;
-
- /* second, apply the old_file delta to sanity check the result */
- if ((error = apply_binary_delta(&reverse, out->ptr, out->size,
- &patch->binary.old_file)) < 0)
- goto done;
-
- if (source_len != reverse.size ||
- memcmp(source, reverse.ptr, source_len) != 0) {
- error = apply_err("binary patch did not apply cleanly");
- goto done;
- }
-
-done:
- if (error < 0)
- git_buf_free(out);
-
- git_buf_free(&reverse);
- return error;
-}
-
-int git_apply__patch(
- git_buf *contents_out,
- char **filename_out,
- unsigned int *mode_out,
- const char *source,
- size_t source_len,
- git_patch *patch)
-{
- char *filename = NULL;
- unsigned int mode = 0;
- int error = 0;
-
- assert(contents_out && filename_out && mode_out && (source || !source_len) && patch);
-
- *filename_out = NULL;
- *mode_out = 0;
-
- if (patch->delta->status != GIT_DELTA_DELETED) {
- const git_diff_file *newfile = &patch->delta->new_file;
-
- filename = git__strdup(newfile->path);
- mode = newfile->mode ?
- newfile->mode : GIT_FILEMODE_BLOB;
- }
-
- if (patch->delta->flags & GIT_DIFF_FLAG_BINARY)
- error = apply_binary(contents_out, source, source_len, patch);
- else if (patch->hunks.size)
- error = apply_hunks(contents_out, source, source_len, patch);
- else
- error = git_buf_put(contents_out, source, source_len);
-
- if (error)
- goto done;
-
- if (patch->delta->status == GIT_DELTA_DELETED &&
- git_buf_len(contents_out) > 0) {
- error = apply_err("removal patch leaves file contents");
- goto done;
- }
-
- *filename_out = filename;
- *mode_out = mode;
-
-done:
- if (error < 0)
- git__free(filename);
-
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_apply_h__
-#define INCLUDE_apply_h__
-
-#include "git2/patch.h"
-#include "buffer.h"
-
-extern int git_apply__patch(
- git_buf *out,
- char **filename,
- unsigned int *mode,
- const char *source,
- size_t source_len,
- git_patch *patch);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_array_h__
-#define INCLUDE_array_h__
-
-#include "common.h"
-
-/*
- * Use this to declare a typesafe resizable array of items, a la:
- *
- * git_array_t(int) my_ints = GIT_ARRAY_INIT;
- * ...
- * int *i = git_array_alloc(my_ints);
- * GITERR_CHECK_ALLOC(i);
- * ...
- * git_array_clear(my_ints);
- *
- * You may also want to do things like:
- *
- * typedef git_array_t(my_struct) my_struct_array_t;
- */
-#define git_array_t(type) struct { type *ptr; size_t size, asize; }
-
-#define GIT_ARRAY_INIT { NULL, 0, 0 }
-
-#define git_array_init(a) \
- do { (a).size = (a).asize = 0; (a).ptr = NULL; } while (0)
-
-#define git_array_init_to_size(a, desired) \
- do { (a).size = 0; (a).asize = desired; (a).ptr = git__calloc(desired, sizeof(*(a).ptr)); } while (0)
-
-#define git_array_clear(a) \
- do { git__free((a).ptr); git_array_init(a); } while (0)
-
-#define GITERR_CHECK_ARRAY(a) GITERR_CHECK_ALLOC((a).ptr)
-
-
-typedef git_array_t(char) git_array_generic_t;
-
-/* use a generic array for growth so this can return the new item */
-GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
-{
- volatile git_array_generic_t *a = _a;
- size_t new_size;
- char *new_array;
-
- if (a->size < 8) {
- new_size = 8;
- } else {
- if (GIT_MULTIPLY_SIZET_OVERFLOW(&new_size, a->size, 3))
- goto on_oom;
- new_size /= 2;
- }
-
- if ((new_array = git__reallocarray(a->ptr, new_size, item_size)) == NULL)
- goto on_oom;
-
- a->ptr = new_array; a->asize = new_size; a->size++;
- return a->ptr + (a->size - 1) * item_size;
-
-on_oom:
- git_array_clear(*a);
- return NULL;
-}
-
-#define git_array_alloc(a) \
- (((a).size >= (a).asize) ? \
- git_array_grow(&(a), sizeof(*(a).ptr)) : \
- ((a).ptr ? &(a).ptr[(a).size++] : NULL))
-
-#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL)
-
-#define git_array_pop(a) ((a).size ? &(a).ptr[--(a).size] : NULL)
-
-#define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : NULL)
-
-#define git_array_size(a) (a).size
-
-#define git_array_valid_index(a, i) ((i) < (a).size)
-
-#define git_array_foreach(a, i, element) \
- for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++)
-
-GIT_INLINE(int) git_array__search(
- size_t *out,
- void *array_ptr,
- size_t item_size,
- size_t array_len,
- int (*compare)(const void *, const void *),
- const void *key)
-{
- size_t lim;
- unsigned char *part, *array = array_ptr, *base = array_ptr;
- int cmp = -1;
-
- for (lim = array_len; lim != 0; lim >>= 1) {
- part = base + (lim >> 1) * item_size;
- cmp = (*compare)(key, part);
-
- if (cmp == 0) {
- base = part;
- break;
- }
- if (cmp > 0) { /* key > p; take right partition */
- base = part + 1 * item_size;
- lim--;
- } /* else take left partition */
- }
-
- if (out)
- *out = (base - array) / item_size;
-
- return (cmp == 0) ? 0 : GIT_ENOTFOUND;
-}
-
-#define git_array_search(out, a, cmp, key) \
- git_array__search(out, (a).ptr, sizeof(*(a).ptr), (a).size, \
- (cmp), (key))
-
-#endif
+++ /dev/null
-#include "common.h"
-#include "repository.h"
-#include "sysdir.h"
-#include "config.h"
-#include "attr_file.h"
-#include "ignore.h"
-#include "git2/oid.h"
-#include <ctype.h>
-
-GIT__USE_STRMAP
-
-const char *git_attr__true = "[internal]__TRUE__";
-const char *git_attr__false = "[internal]__FALSE__";
-const char *git_attr__unset = "[internal]__UNSET__";
-
-git_attr_t git_attr_value(const char *attr)
-{
- if (attr == NULL || attr == git_attr__unset)
- return GIT_ATTR_UNSPECIFIED_T;
-
- if (attr == git_attr__true)
- return GIT_ATTR_TRUE_T;
-
- if (attr == git_attr__false)
- return GIT_ATTR_FALSE_T;
-
- return GIT_ATTR_VALUE_T;
-}
-
-static int collect_attr_files(
- git_repository *repo,
- git_attr_session *attr_session,
- uint32_t flags,
- const char *path,
- git_vector *files);
-
-static void release_attr_files(git_vector *files);
-
-int git_attr_get(
- const char **value,
- git_repository *repo,
- uint32_t flags,
- const char *pathname,
- const char *name)
-{
- int error;
- git_attr_path path;
- git_vector files = GIT_VECTOR_INIT;
- size_t i, j;
- git_attr_file *file;
- git_attr_name attr;
- git_attr_rule *rule;
-
- assert(value && repo && name);
-
- *value = NULL;
-
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), GIT_DIR_FLAG_UNKNOWN) < 0)
- return -1;
-
- if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0)
- goto cleanup;
-
- memset(&attr, 0, sizeof(attr));
- attr.name = name;
- attr.name_hash = git_attr_file__name_hash(name);
-
- git_vector_foreach(&files, i, file) {
-
- git_attr_file__foreach_matching_rule(file, &path, j, rule) {
- size_t pos;
-
- if (!git_vector_bsearch(&pos, &rule->assigns, &attr)) {
- *value = ((git_attr_assignment *)git_vector_get(
- &rule->assigns, pos))->value;
- goto cleanup;
- }
- }
- }
-
-cleanup:
- release_attr_files(&files);
- git_attr_path__free(&path);
-
- return error;
-}
-
-
-typedef struct {
- git_attr_name name;
- git_attr_assignment *found;
-} attr_get_many_info;
-
-int git_attr_get_many_with_session(
- const char **values,
- git_repository *repo,
- git_attr_session *attr_session,
- uint32_t flags,
- const char *pathname,
- size_t num_attr,
- const char **names)
-{
- int error;
- git_attr_path path;
- git_vector files = GIT_VECTOR_INIT;
- size_t i, j, k;
- git_attr_file *file;
- git_attr_rule *rule;
- attr_get_many_info *info = NULL;
- size_t num_found = 0;
-
- if (!num_attr)
- return 0;
-
- assert(values && repo && names);
-
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), GIT_DIR_FLAG_UNKNOWN) < 0)
- return -1;
-
- if ((error = collect_attr_files(repo, attr_session, flags, pathname, &files)) < 0)
- goto cleanup;
-
- info = git__calloc(num_attr, sizeof(attr_get_many_info));
- GITERR_CHECK_ALLOC(info);
-
- git_vector_foreach(&files, i, file) {
-
- git_attr_file__foreach_matching_rule(file, &path, j, rule) {
-
- for (k = 0; k < num_attr; k++) {
- size_t pos;
-
- if (info[k].found != NULL) /* already found assignment */
- continue;
-
- if (!info[k].name.name) {
- info[k].name.name = names[k];
- info[k].name.name_hash = git_attr_file__name_hash(names[k]);
- }
-
- if (!git_vector_bsearch(&pos, &rule->assigns, &info[k].name)) {
- info[k].found = (git_attr_assignment *)
- git_vector_get(&rule->assigns, pos);
- values[k] = info[k].found->value;
-
- if (++num_found == num_attr)
- goto cleanup;
- }
- }
- }
- }
-
- for (k = 0; k < num_attr; k++) {
- if (!info[k].found)
- values[k] = NULL;
- }
-
-cleanup:
- release_attr_files(&files);
- git_attr_path__free(&path);
- git__free(info);
-
- return error;
-}
-
-int git_attr_get_many(
- const char **values,
- git_repository *repo,
- uint32_t flags,
- const char *pathname,
- size_t num_attr,
- const char **names)
-{
- return git_attr_get_many_with_session(
- values, repo, NULL, flags, pathname, num_attr, names);
-}
-
-int git_attr_foreach(
- git_repository *repo,
- uint32_t flags,
- const char *pathname,
- int (*callback)(const char *name, const char *value, void *payload),
- void *payload)
-{
- int error;
- git_attr_path path;
- git_vector files = GIT_VECTOR_INIT;
- size_t i, j, k;
- git_attr_file *file;
- git_attr_rule *rule;
- git_attr_assignment *assign;
- git_strmap *seen = NULL;
-
- assert(repo && callback);
-
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), GIT_DIR_FLAG_UNKNOWN) < 0)
- return -1;
-
- if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0 ||
- (error = git_strmap_alloc(&seen)) < 0)
- goto cleanup;
-
- git_vector_foreach(&files, i, file) {
-
- git_attr_file__foreach_matching_rule(file, &path, j, rule) {
-
- git_vector_foreach(&rule->assigns, k, assign) {
- /* skip if higher priority assignment was already seen */
- if (git_strmap_exists(seen, assign->name))
- continue;
-
- git_strmap_insert(seen, assign->name, assign, error);
- if (error < 0)
- goto cleanup;
-
- error = callback(assign->name, assign->value, payload);
- if (error) {
- giterr_set_after_callback(error);
- goto cleanup;
- }
- }
- }
- }
-
-cleanup:
- git_strmap_free(seen);
- release_attr_files(&files);
- git_attr_path__free(&path);
-
- return error;
-}
-
-static int preload_attr_file(
- git_repository *repo,
- git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *file)
-{
- int error;
- git_attr_file *preload = NULL;
-
- if (!file)
- return 0;
- if (!(error = git_attr_cache__get(
- &preload, repo, attr_session, source, base, file, git_attr_file__parse_buffer)))
- git_attr_file__free(preload);
-
- return error;
-}
-
-static int system_attr_file(
- git_buf *out,
- git_attr_session *attr_session)
-{
- int error;
-
- if (!attr_session) {
- error = git_sysdir_find_system_file(out, GIT_ATTR_FILE_SYSTEM);
-
- if (error == GIT_ENOTFOUND)
- giterr_clear();
-
- return error;
- }
-
- if (!attr_session->init_sysdir) {
- error = git_sysdir_find_system_file(&attr_session->sysdir, GIT_ATTR_FILE_SYSTEM);
-
- if (error == GIT_ENOTFOUND)
- giterr_clear();
- else if (error)
- return error;
-
- attr_session->init_sysdir = 1;
- }
-
- if (attr_session->sysdir.size == 0)
- return GIT_ENOTFOUND;
-
- /* We can safely provide a git_buf with no allocation (asize == 0) to
- * a consumer. This allows them to treat this as a regular `git_buf`,
- * but their call to `git_buf_free` will not attempt to free it.
- */
- git_buf_attach_notowned(
- out, attr_session->sysdir.ptr, attr_session->sysdir.size);
- return 0;
-}
-
-static int attr_setup(git_repository *repo, git_attr_session *attr_session)
-{
- int error = 0;
- const char *workdir = git_repository_workdir(repo);
- git_index *idx = NULL;
- git_buf sys = GIT_BUF_INIT;
-
- if (attr_session && attr_session->init_setup)
- return 0;
-
- if ((error = git_attr_cache__init(repo)) < 0)
- return error;
-
- /* preload attribute files that could contain macros so the
- * definitions will be available for later file parsing
- */
-
- error = system_attr_file(&sys, attr_session);
-
- if (error == 0)
- error = preload_attr_file(
- repo, attr_session, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr);
-
- if (error != GIT_ENOTFOUND)
- return error;
-
- git_buf_free(&sys);
-
- if ((error = preload_attr_file(
- repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
- NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
- return error;
-
- if ((error = preload_attr_file(
- repo, attr_session, GIT_ATTR_FILE__FROM_FILE,
- git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0)
- return error;
-
- if (workdir != NULL &&
- (error = preload_attr_file(
- repo, attr_session, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0)
- return error;
-
- if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
- (error = preload_attr_file(
- repo, attr_session, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0)
- return error;
-
- if (attr_session)
- attr_session->init_setup = 1;
-
- return error;
-}
-
-int git_attr_add_macro(
- git_repository *repo,
- const char *name,
- const char *values)
-{
- int error;
- git_attr_rule *macro = NULL;
- git_pool *pool;
-
- if ((error = git_attr_cache__init(repo)) < 0)
- return error;
-
- macro = git__calloc(1, sizeof(git_attr_rule));
- GITERR_CHECK_ALLOC(macro);
-
- pool = &git_repository_attr_cache(repo)->pool;
-
- macro->match.pattern = git_pool_strdup(pool, name);
- GITERR_CHECK_ALLOC(macro->match.pattern);
-
- macro->match.length = strlen(macro->match.pattern);
- macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
-
- error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values);
-
- if (!error)
- error = git_attr_cache__insert_macro(repo, macro);
-
- if (error < 0)
- git_attr_rule__free(macro);
-
- return error;
-}
-
-typedef struct {
- git_repository *repo;
- git_attr_session *attr_session;
- uint32_t flags;
- const char *workdir;
- git_index *index;
- git_vector *files;
-} attr_walk_up_info;
-
-static int attr_decide_sources(
- uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)
-{
- int count = 0;
-
- switch (flags & 0x03) {
- case GIT_ATTR_CHECK_FILE_THEN_INDEX:
- if (has_wd)
- srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
- if (has_index)
- srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
- break;
- case GIT_ATTR_CHECK_INDEX_THEN_FILE:
- if (has_index)
- srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
- if (has_wd)
- srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
- break;
- case GIT_ATTR_CHECK_INDEX_ONLY:
- if (has_index)
- srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
- break;
- }
-
- return count;
-}
-
-static int push_attr_file(
- git_repository *repo,
- git_attr_session *attr_session,
- git_vector *list,
- git_attr_file_source source,
- const char *base,
- const char *filename)
-{
- int error = 0;
- git_attr_file *file = NULL;
-
- error = git_attr_cache__get(&file, repo, attr_session,
- source, base, filename, git_attr_file__parse_buffer);
-
- if (error < 0)
- return error;
-
- if (file != NULL) {
- if ((error = git_vector_insert(list, file)) < 0)
- git_attr_file__free(file);
- }
-
- return error;
-}
-
-static int push_one_attr(void *ref, const char *path)
-{
- int error = 0, n_src, i;
- attr_walk_up_info *info = (attr_walk_up_info *)ref;
- git_attr_file_source src[2];
-
- n_src = attr_decide_sources(
- info->flags, info->workdir != NULL, info->index != NULL, src);
-
- for (i = 0; !error && i < n_src; ++i)
- error = push_attr_file(info->repo, info->attr_session,
- info->files, src[i], path, GIT_ATTR_FILE);
-
- return error;
-}
-
-static void release_attr_files(git_vector *files)
-{
- size_t i;
- git_attr_file *file;
-
- git_vector_foreach(files, i, file) {
- git_attr_file__free(file);
- files->contents[i] = NULL;
- }
- git_vector_free(files);
-}
-
-static int collect_attr_files(
- git_repository *repo,
- git_attr_session *attr_session,
- uint32_t flags,
- const char *path,
- git_vector *files)
-{
- int error = 0;
- git_buf dir = GIT_BUF_INIT;
- const char *workdir = git_repository_workdir(repo);
- attr_walk_up_info info = { NULL };
-
- if ((error = attr_setup(repo, attr_session)) < 0)
- return error;
-
- /* Resolve path in a non-bare repo */
- if (workdir != NULL)
- error = git_path_find_dir(&dir, path, workdir);
- else
- error = git_path_dirname_r(&dir, path);
- if (error < 0)
- goto cleanup;
-
- /* in precendence order highest to lowest:
- * - $GIT_DIR/info/attributes
- * - path components with .gitattributes
- * - config core.attributesfile
- * - $GIT_PREFIX/etc/gitattributes
- */
-
- error = push_attr_file(
- repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
- git_repository_path(repo), GIT_ATTR_FILE_INREPO);
- if (error < 0)
- goto cleanup;
-
- info.repo = repo;
- info.attr_session = attr_session;
- info.flags = flags;
- info.workdir = workdir;
- if (git_repository_index__weakptr(&info.index, repo) < 0)
- giterr_clear(); /* no error even if there is no index */
- info.files = files;
-
- if (!strcmp(dir.ptr, "."))
- error = push_one_attr(&info, "");
- else
- error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
-
- if (error < 0)
- goto cleanup;
-
- if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
- error = push_attr_file(
- repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
- NULL, git_repository_attr_cache(repo)->cfg_attr_file);
- if (error < 0)
- goto cleanup;
- }
-
- if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
- error = system_attr_file(&dir, attr_session);
-
- if (!error)
- error = push_attr_file(
- repo, attr_session, files, GIT_ATTR_FILE__FROM_FILE,
- NULL, dir.ptr);
- else if (error == GIT_ENOTFOUND)
- error = 0;
- }
-
- cleanup:
- if (error < 0)
- release_attr_files(files);
- git_buf_free(&dir);
-
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_attr_h__
-#define INCLUDE_attr_h__
-
-#include "attr_file.h"
-#include "attrcache.h"
-
-#endif
+++ /dev/null
-#include "common.h"
-#include "repository.h"
-#include "filebuf.h"
-#include "attr_file.h"
-#include "attrcache.h"
-#include "git2/blob.h"
-#include "git2/tree.h"
-#include "index.h"
-#include <ctype.h>
-
-static void attr_file_free(git_attr_file *file)
-{
- bool unlock = !git_mutex_lock(&file->lock);
- git_attr_file__clear_rules(file, false);
- git_pool_clear(&file->pool);
- if (unlock)
- git_mutex_unlock(&file->lock);
- git_mutex_free(&file->lock);
-
- git__memzero(file, sizeof(*file));
- git__free(file);
-}
-
-int git_attr_file__new(
- git_attr_file **out,
- git_attr_file_entry *entry,
- git_attr_file_source source)
-{
- git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file));
- GITERR_CHECK_ALLOC(attrs);
-
- if (git_mutex_init(&attrs->lock) < 0) {
- giterr_set(GITERR_OS, "Failed to initialize lock");
- git__free(attrs);
- return -1;
- }
-
- git_pool_init(&attrs->pool, 1);
- GIT_REFCOUNT_INC(attrs);
- attrs->entry = entry;
- attrs->source = source;
- *out = attrs;
- return 0;
-}
-
-int git_attr_file__clear_rules(git_attr_file *file, bool need_lock)
-{
- unsigned int i;
- git_attr_rule *rule;
-
- if (need_lock && git_mutex_lock(&file->lock) < 0) {
- giterr_set(GITERR_OS, "Failed to lock attribute file");
- return -1;
- }
-
- git_vector_foreach(&file->rules, i, rule)
- git_attr_rule__free(rule);
- git_vector_free(&file->rules);
-
- if (need_lock)
- git_mutex_unlock(&file->lock);
-
- return 0;
-}
-
-void git_attr_file__free(git_attr_file *file)
-{
- if (!file)
- return;
- GIT_REFCOUNT_DEC(file, attr_file_free);
-}
-
-static int attr_file_oid_from_index(
- git_oid *oid, git_repository *repo, const char *path)
-{
- int error;
- git_index *idx;
- size_t pos;
- const git_index_entry *entry;
-
- if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
- (error = git_index__find_pos(&pos, idx, path, 0, 0)) < 0)
- return error;
-
- if (!(entry = git_index_get_byindex(idx, pos)))
- return GIT_ENOTFOUND;
-
- *oid = entry->id;
- return 0;
-}
-
-int git_attr_file__load(
- git_attr_file **out,
- git_repository *repo,
- git_attr_session *attr_session,
- git_attr_file_entry *entry,
- git_attr_file_source source,
- git_attr_file_parser parser)
-{
- int error = 0;
- git_blob *blob = NULL;
- git_buf content = GIT_BUF_INIT;
- git_attr_file *file;
- struct stat st;
- bool nonexistent = false;
-
- *out = NULL;
-
- switch (source) {
- case GIT_ATTR_FILE__IN_MEMORY:
- /* in-memory attribute file doesn't need data */
- break;
- case GIT_ATTR_FILE__FROM_INDEX: {
- git_oid id;
-
- if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
- (error = git_blob_lookup(&blob, repo, &id)) < 0)
- return error;
-
- /* Do not assume that data straight from the ODB is NULL-terminated;
- * copy the contents of a file to a buffer to work on */
- git_buf_put(&content, git_blob_rawcontent(blob), git_blob_rawsize(blob));
- break;
- }
- case GIT_ATTR_FILE__FROM_FILE: {
- int fd = -1;
-
- /* For open or read errors, pretend that we got ENOTFOUND. */
- /* TODO: issue warning when warning API is available */
-
- if (p_stat(entry->fullpath, &st) < 0 ||
- S_ISDIR(st.st_mode) ||
- (fd = git_futils_open_ro(entry->fullpath)) < 0 ||
- (error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0)
- nonexistent = true;
-
- if (fd >= 0)
- p_close(fd);
-
- break;
- }
- default:
- giterr_set(GITERR_INVALID, "Unknown file source %d", source);
- return -1;
- }
-
- if ((error = git_attr_file__new(&file, entry, source)) < 0)
- goto cleanup;
-
- /* store the key of the attr_reader; don't bother with cache
- * invalidation during the same attr reader session.
- */
- if (attr_session)
- file->session_key = attr_session->key;
-
- if (parser && (error = parser(repo, file, git_buf_cstr(&content))) < 0) {
- git_attr_file__free(file);
- goto cleanup;
- }
-
- /* write cache breakers */
- if (nonexistent)
- file->nonexistent = 1;
- else if (source == GIT_ATTR_FILE__FROM_INDEX)
- git_oid_cpy(&file->cache_data.oid, git_blob_id(blob));
- else if (source == GIT_ATTR_FILE__FROM_FILE)
- git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st);
- /* else always cacheable */
-
- *out = file;
-
-cleanup:
- git_blob_free(blob);
- git_buf_free(&content);
-
- return error;
-}
-
-int git_attr_file__out_of_date(
- git_repository *repo,
- git_attr_session *attr_session,
- git_attr_file *file)
-{
- if (!file)
- return 1;
-
- /* we are never out of date if we just created this data in the same
- * attr_session; otherwise, nonexistent files must be invalidated
- */
- if (attr_session && attr_session->key == file->session_key)
- return 0;
- else if (file->nonexistent)
- return 1;
-
- switch (file->source) {
- case GIT_ATTR_FILE__IN_MEMORY:
- return 0;
-
- case GIT_ATTR_FILE__FROM_FILE:
- return git_futils_filestamp_check(
- &file->cache_data.stamp, file->entry->fullpath);
-
- case GIT_ATTR_FILE__FROM_INDEX: {
- int error;
- git_oid id;
-
- if ((error = attr_file_oid_from_index(
- &id, repo, file->entry->path)) < 0)
- return error;
-
- return (git_oid__cmp(&file->cache_data.oid, &id) != 0);
- }
-
- default:
- giterr_set(GITERR_INVALID, "Invalid file type %d", file->source);
- return -1;
- }
-}
-
-static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
-static void git_attr_rule__clear(git_attr_rule *rule);
-static bool parse_optimized_patterns(
- git_attr_fnmatch *spec,
- git_pool *pool,
- const char *pattern);
-
-int git_attr_file__parse_buffer(
- git_repository *repo, git_attr_file *attrs, const char *data)
-{
- int error = 0;
- const char *scan = data, *context = NULL;
- git_attr_rule *rule = NULL;
-
- /* if subdir file path, convert context for file paths */
- if (attrs->entry &&
- git_path_root(attrs->entry->path) < 0 &&
- !git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE))
- context = attrs->entry->path;
-
- if (git_mutex_lock(&attrs->lock) < 0) {
- giterr_set(GITERR_OS, "Failed to lock attribute file");
- return -1;
- }
-
- while (!error && *scan) {
- /* allocate rule if needed */
- if (!rule && !(rule = git__calloc(1, sizeof(*rule)))) {
- error = -1;
- break;
- }
-
- rule->match.flags =
- GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO;
-
- /* parse the next "pattern attr attr attr" line */
- if (!(error = git_attr_fnmatch__parse(
- &rule->match, &attrs->pool, context, &scan)) &&
- !(error = git_attr_assignment__parse(
- repo, &attrs->pool, &rule->assigns, &scan)))
- {
- if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
- /* TODO: warning if macro found in file below repo root */
- error = git_attr_cache__insert_macro(repo, rule);
- else
- error = git_vector_insert(&attrs->rules, rule);
- }
-
- /* if the rule wasn't a pattern, on to the next */
- if (error < 0) {
- git_attr_rule__clear(rule); /* reset rule contents */
- if (error == GIT_ENOTFOUND)
- error = 0;
- } else {
- rule = NULL; /* vector now "owns" the rule */
- }
- }
-
- git_mutex_unlock(&attrs->lock);
- git_attr_rule__free(rule);
-
- return error;
-}
-
-uint32_t git_attr_file__name_hash(const char *name)
-{
- uint32_t h = 5381;
- int c;
- assert(name);
- while ((c = (int)*name++) != 0)
- h = ((h << 5) + h) + c;
- return h;
-}
-
-int git_attr_file__lookup_one(
- git_attr_file *file,
- git_attr_path *path,
- const char *attr,
- const char **value)
-{
- size_t i;
- git_attr_name name;
- git_attr_rule *rule;
-
- *value = NULL;
-
- name.name = attr;
- name.name_hash = git_attr_file__name_hash(attr);
-
- git_attr_file__foreach_matching_rule(file, path, i, rule) {
- size_t pos;
-
- if (!git_vector_bsearch(&pos, &rule->assigns, &name)) {
- *value = ((git_attr_assignment *)
- git_vector_get(&rule->assigns, pos))->value;
- break;
- }
- }
-
- return 0;
-}
-
-int git_attr_file__load_standalone(git_attr_file **out, const char *path)
-{
- int error;
- git_attr_file *file;
- git_buf content = GIT_BUF_INIT;
-
- error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE);
- if (error < 0)
- return error;
-
- error = git_attr_cache__alloc_file_entry(
- &file->entry, NULL, path, &file->pool);
- if (error < 0) {
- git_attr_file__free(file);
- return error;
- }
- /* because the cache entry is allocated from the file's own pool, we
- * don't have to free it - freeing file+pool will free cache entry, too.
- */
-
- if (!(error = git_futils_readbuffer(&content, path))) {
- error = git_attr_file__parse_buffer(NULL, file, content.ptr);
- git_buf_free(&content);
- }
-
- if (error < 0)
- git_attr_file__free(file);
- else
- *out = file;
-
- return error;
-}
-
-bool git_attr_fnmatch__match(
- git_attr_fnmatch *match,
- git_attr_path *path)
-{
- const char *relpath = path->path;
- const char *filename;
- int flags = 0;
-
- /*
- * If the rule was generated in a subdirectory, we must only
- * use it for paths inside that directory. We can thus return
- * a non-match if the prefixes don't match.
- */
- if (match->containing_dir) {
- if (match->flags & GIT_ATTR_FNMATCH_ICASE) {
- if (git__strncasecmp(path->path, match->containing_dir, match->containing_dir_length))
- return 0;
- } else {
- if (git__prefixcmp(path->path, match->containing_dir))
- return 0;
- }
-
- relpath += match->containing_dir_length;
- }
-
- if (match->flags & GIT_ATTR_FNMATCH_ICASE)
- flags |= FNM_CASEFOLD;
- if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR)
- flags |= FNM_LEADING_DIR;
-
- if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
- filename = relpath;
- flags |= FNM_PATHNAME;
- } else {
- filename = path->basename;
-
- if (path->is_dir)
- flags |= FNM_LEADING_DIR;
- }
-
- if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
- bool samename;
-
- /* for attribute checks or root ignore checks, fail match */
- if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) ||
- path->basename == path->path)
- return false;
-
- flags |= FNM_LEADING_DIR;
-
- /* fail match if this is a file with same name as ignored folder */
- samename = (match->flags & GIT_ATTR_FNMATCH_ICASE) ?
- !strcasecmp(match->pattern, relpath) :
- !strcmp(match->pattern, relpath);
-
- if (samename)
- return false;
-
- return (p_fnmatch(match->pattern, relpath, flags) != FNM_NOMATCH);
- }
-
- /* if path is a directory prefix of a negated pattern, then match */
- if ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) && path->is_dir) {
- size_t pathlen = strlen(relpath);
- bool prefixed = (pathlen <= match->length) &&
- ((match->flags & GIT_ATTR_FNMATCH_ICASE) ?
- !strncasecmp(match->pattern, relpath, pathlen) :
- !strncmp(match->pattern, relpath, pathlen));
-
- if (prefixed && git_path_at_end_of_segment(&match->pattern[pathlen]))
- return true;
- }
-
- return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH);
-}
-
-bool git_attr_rule__match(
- git_attr_rule *rule,
- git_attr_path *path)
-{
- bool matched = git_attr_fnmatch__match(&rule->match, path);
-
- if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE)
- matched = !matched;
-
- return matched;
-}
-
-git_attr_assignment *git_attr_rule__lookup_assignment(
- git_attr_rule *rule, const char *name)
-{
- size_t pos;
- git_attr_name key;
- key.name = name;
- key.name_hash = git_attr_file__name_hash(name);
-
- if (git_vector_bsearch(&pos, &rule->assigns, &key))
- return NULL;
-
- return git_vector_get(&rule->assigns, pos);
-}
-
-int git_attr_path__init(
- git_attr_path *info, const char *path, const char *base, git_dir_flag dir_flag)
-{
- ssize_t root;
-
- /* build full path as best we can */
- git_buf_init(&info->full, 0);
-
- if (git_path_join_unrooted(&info->full, path, base, &root) < 0)
- return -1;
-
- info->path = info->full.ptr + root;
-
- /* remove trailing slashes */
- while (info->full.size > 0) {
- if (info->full.ptr[info->full.size - 1] != '/')
- break;
- info->full.size--;
- }
- info->full.ptr[info->full.size] = '\0';
-
- /* skip leading slashes in path */
- while (*info->path == '/')
- info->path++;
-
- /* find trailing basename component */
- info->basename = strrchr(info->path, '/');
- if (info->basename)
- info->basename++;
- if (!info->basename || !*info->basename)
- info->basename = info->path;
-
- switch (dir_flag)
- {
- case GIT_DIR_FLAG_FALSE:
- info->is_dir = 0;
- break;
-
- case GIT_DIR_FLAG_TRUE:
- info->is_dir = 1;
- break;
-
- case GIT_DIR_FLAG_UNKNOWN:
- default:
- info->is_dir = (int)git_path_isdir(info->full.ptr);
- break;
- }
-
- return 0;
-}
-
-void git_attr_path__free(git_attr_path *info)
-{
- git_buf_free(&info->full);
- info->path = NULL;
- info->basename = NULL;
-}
-
-/*
- * From gitattributes(5):
- *
- * Patterns have the following format:
- *
- * - A blank line matches no files, so it can serve as a separator for
- * readability.
- *
- * - A line starting with # serves as a comment.
- *
- * - An optional prefix ! which negates the pattern; any matching file
- * excluded by a previous pattern will become included again. If a negated
- * pattern matches, this will override lower precedence patterns sources.
- *
- * - If the pattern ends with a slash, it is removed for the purpose of the
- * following description, but it would only find a match with a directory. In
- * other words, foo/ will match a directory foo and paths underneath it, but
- * will not match a regular file or a symbolic link foo (this is consistent
- * with the way how pathspec works in general in git).
- *
- * - If the pattern does not contain a slash /, git treats it as a shell glob
- * pattern and checks for a match against the pathname without leading
- * directories.
- *
- * - Otherwise, git treats the pattern as a shell glob suitable for consumption
- * by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will
- * not match a / in the pathname. For example, "Documentation/\*.html" matches
- * "Documentation/git.html" but not "Documentation/ppc/ppc.html". A leading
- * slash matches the beginning of the pathname; for example, "/\*.c" matches
- * "cat-file.c" but not "mozilla-sha1/sha1.c".
- */
-
-/*
- * This will return 0 if the spec was filled out,
- * GIT_ENOTFOUND if the fnmatch does not require matching, or
- * another error code there was an actual problem.
- */
-int git_attr_fnmatch__parse(
- git_attr_fnmatch *spec,
- git_pool *pool,
- const char *context,
- const char **base)
-{
- const char *pattern, *scan;
- int slash_count, allow_space;
-
- assert(spec && base && *base);
-
- if (parse_optimized_patterns(spec, pool, *base))
- return 0;
-
- spec->flags = (spec->flags & GIT_ATTR_FNMATCH__INCOMING);
- allow_space = ((spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0);
-
- pattern = *base;
-
- while (git__isspace(*pattern)) pattern++;
- if (!*pattern || *pattern == '#') {
- *base = git__next_line(pattern);
- return GIT_ENOTFOUND;
- }
-
- if (*pattern == '[' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWMACRO) != 0) {
- if (strncmp(pattern, "[attr]", 6) == 0) {
- spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;
- pattern += 6;
- }
- /* else a character range like [a-e]* which is accepted */
- }
-
- if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
- spec->flags = spec->flags |
- GIT_ATTR_FNMATCH_NEGATIVE | GIT_ATTR_FNMATCH_LEADINGDIR;
- pattern++;
- }
-
- slash_count = 0;
- for (scan = pattern; *scan != '\0'; ++scan) {
- /* scan until (non-escaped) white space */
- if (git__isspace(*scan) && *(scan - 1) != '\\') {
- if (!allow_space || (*scan != ' ' && *scan != '\t' && *scan != '\r'))
- break;
- }
-
- if (*scan == '/') {
- spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
- slash_count++;
- if (pattern == scan)
- pattern++;
- }
- /* remember if we see an unescaped wildcard in pattern */
- else if (git__iswildcard(*scan) &&
- (scan == pattern || (*(scan - 1) != '\\')))
- spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
- }
-
- *base = scan;
-
- if ((spec->length = scan - pattern) == 0)
- return GIT_ENOTFOUND;
-
- /*
- * Remove one trailing \r in case this is a CRLF delimited
- * file, in the case of Icon\r\r\n, we still leave the first
- * \r there to match against.
- */
- if (pattern[spec->length - 1] == '\r')
- if (--spec->length == 0)
- return GIT_ENOTFOUND;
-
- if (pattern[spec->length - 1] == '/') {
- spec->length--;
- spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY;
- if (--slash_count <= 0)
- spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;
- }
- if ((spec->flags & GIT_ATTR_FNMATCH_NOLEADINGDIR) == 0 &&
- spec->length >= 2 &&
- pattern[spec->length - 1] == '*' &&
- pattern[spec->length - 2] == '/') {
- spec->length -= 2;
- spec->flags = spec->flags | GIT_ATTR_FNMATCH_LEADINGDIR;
- /* leave FULLPATH match on, however */
- }
-
- if (context) {
- char *slash = strrchr(context, '/');
- size_t len;
- if (slash) {
- /* include the slash for easier matching */
- len = slash - context + 1;
- spec->containing_dir = git_pool_strndup(pool, context, len);
- spec->containing_dir_length = len;
- }
- }
-
- spec->pattern = git_pool_strndup(pool, pattern, spec->length);
-
- if (!spec->pattern) {
- *base = git__next_line(pattern);
- return -1;
- } else {
- /* strip '\' that might have be used for internal whitespace */
- spec->length = git__unescape(spec->pattern);
- /* TODO: convert remaining '\' into '/' for POSIX ??? */
- }
-
- return 0;
-}
-
-static bool parse_optimized_patterns(
- git_attr_fnmatch *spec,
- git_pool *pool,
- const char *pattern)
-{
- if (!pattern[1] && (pattern[0] == '*' || pattern[0] == '.')) {
- spec->flags = GIT_ATTR_FNMATCH_MATCH_ALL;
- spec->pattern = git_pool_strndup(pool, pattern, 1);
- spec->length = 1;
-
- return true;
- }
-
- return false;
-}
-
-static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
-{
- const git_attr_name *a = a_raw;
- const git_attr_name *b = b_raw;
-
- if (b->name_hash < a->name_hash)
- return 1;
- else if (b->name_hash > a->name_hash)
- return -1;
- else
- return strcmp(b->name, a->name);
-}
-
-static void git_attr_assignment__free(git_attr_assignment *assign)
-{
- /* name and value are stored in a git_pool associated with the
- * git_attr_file, so they do not need to be freed here
- */
- assign->name = NULL;
- assign->value = NULL;
- git__free(assign);
-}
-
-static int merge_assignments(void **old_raw, void *new_raw)
-{
- git_attr_assignment **old = (git_attr_assignment **)old_raw;
- git_attr_assignment *new = (git_attr_assignment *)new_raw;
-
- GIT_REFCOUNT_DEC(*old, git_attr_assignment__free);
- *old = new;
- return GIT_EEXISTS;
-}
-
-int git_attr_assignment__parse(
- git_repository *repo,
- git_pool *pool,
- git_vector *assigns,
- const char **base)
-{
- int error;
- const char *scan = *base;
- git_attr_assignment *assign = NULL;
-
- assert(assigns && !assigns->length);
-
- git_vector_set_cmp(assigns, sort_by_hash_and_name);
-
- while (*scan && *scan != '\n') {
- const char *name_start, *value_start;
-
- /* skip leading blanks */
- while (git__isspace(*scan) && *scan != '\n') scan++;
-
- /* allocate assign if needed */
- if (!assign) {
- assign = git__calloc(1, sizeof(git_attr_assignment));
- GITERR_CHECK_ALLOC(assign);
- GIT_REFCOUNT_INC(assign);
- }
-
- assign->name_hash = 5381;
- assign->value = git_attr__true;
-
- /* look for magic name prefixes */
- if (*scan == '-') {
- assign->value = git_attr__false;
- scan++;
- } else if (*scan == '!') {
- assign->value = git_attr__unset; /* explicit unspecified state */
- scan++;
- } else if (*scan == '#') /* comment rest of line */
- break;
-
- /* find the name */
- name_start = scan;
- while (*scan && !git__isspace(*scan) && *scan != '=') {
- assign->name_hash =
- ((assign->name_hash << 5) + assign->name_hash) + *scan;
- scan++;
- }
- if (scan == name_start) {
- /* must have found lone prefix (" - ") or leading = ("=foo")
- * or end of buffer -- advance until whitespace and continue
- */
- while (*scan && !git__isspace(*scan)) scan++;
- continue;
- }
-
- /* allocate permanent storage for name */
- assign->name = git_pool_strndup(pool, name_start, scan - name_start);
- GITERR_CHECK_ALLOC(assign->name);
-
- /* if there is an equals sign, find the value */
- if (*scan == '=') {
- for (value_start = ++scan; *scan && !git__isspace(*scan); ++scan);
-
- /* if we found a value, allocate permanent storage for it */
- if (scan > value_start) {
- assign->value = git_pool_strndup(pool, value_start, scan - value_start);
- GITERR_CHECK_ALLOC(assign->value);
- }
- }
-
- /* expand macros (if given a repo with a macro cache) */
- if (repo != NULL && assign->value == git_attr__true) {
- git_attr_rule *macro =
- git_attr_cache__lookup_macro(repo, assign->name);
-
- if (macro != NULL) {
- unsigned int i;
- git_attr_assignment *massign;
-
- git_vector_foreach(¯o->assigns, i, massign) {
- GIT_REFCOUNT_INC(massign);
-
- error = git_vector_insert_sorted(
- assigns, massign, &merge_assignments);
- if (error < 0 && error != GIT_EEXISTS) {
- git_attr_assignment__free(assign);
- return error;
- }
- }
- }
- }
-
- /* insert allocated assign into vector */
- error = git_vector_insert_sorted(assigns, assign, &merge_assignments);
- if (error < 0 && error != GIT_EEXISTS)
- return error;
-
- /* clear assign since it is now "owned" by the vector */
- assign = NULL;
- }
-
- if (assign != NULL)
- git_attr_assignment__free(assign);
-
- *base = git__next_line(scan);
-
- return (assigns->length == 0) ? GIT_ENOTFOUND : 0;
-}
-
-static void git_attr_rule__clear(git_attr_rule *rule)
-{
- unsigned int i;
- git_attr_assignment *assign;
-
- if (!rule)
- return;
-
- if (!(rule->match.flags & GIT_ATTR_FNMATCH_IGNORE)) {
- git_vector_foreach(&rule->assigns, i, assign)
- GIT_REFCOUNT_DEC(assign, git_attr_assignment__free);
- git_vector_free(&rule->assigns);
- }
-
- /* match.pattern is stored in a git_pool, so no need to free */
- rule->match.pattern = NULL;
- rule->match.length = 0;
-}
-
-void git_attr_rule__free(git_attr_rule *rule)
-{
- git_attr_rule__clear(rule);
- git__free(rule);
-}
-
-int git_attr_session__init(git_attr_session *session, git_repository *repo)
-{
- assert(repo);
-
- session->key = git_atomic_inc(&repo->attr_session_key);
-
- return 0;
-}
-
-void git_attr_session__free(git_attr_session *session)
-{
- if (!session)
- return;
-
- git_buf_free(&session->sysdir);
- git_buf_free(&session->tmp);
-
- memset(session, 0, sizeof(git_attr_session));
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_attr_file_h__
-#define INCLUDE_attr_file_h__
-
-#include "git2/oid.h"
-#include "git2/attr.h"
-#include "vector.h"
-#include "pool.h"
-#include "buffer.h"
-#include "fileops.h"
-
-#define GIT_ATTR_FILE ".gitattributes"
-#define GIT_ATTR_FILE_INREPO "info/attributes"
-#define GIT_ATTR_FILE_SYSTEM "gitattributes"
-#define GIT_ATTR_FILE_XDG "attributes"
-
-#define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0)
-#define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1)
-#define GIT_ATTR_FNMATCH_FULLPATH (1U << 2)
-#define GIT_ATTR_FNMATCH_MACRO (1U << 3)
-#define GIT_ATTR_FNMATCH_IGNORE (1U << 4)
-#define GIT_ATTR_FNMATCH_HASWILD (1U << 5)
-#define GIT_ATTR_FNMATCH_ALLOWSPACE (1U << 6)
-#define GIT_ATTR_FNMATCH_ICASE (1U << 7)
-#define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8)
-#define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9)
-#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
-#define GIT_ATTR_FNMATCH_LEADINGDIR (1U << 11)
-#define GIT_ATTR_FNMATCH_NOLEADINGDIR (1U << 12)
-
-#define GIT_ATTR_FNMATCH__INCOMING \
- (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | \
- GIT_ATTR_FNMATCH_ALLOWMACRO | GIT_ATTR_FNMATCH_NOLEADINGDIR)
-
-typedef enum {
- GIT_ATTR_FILE__IN_MEMORY = 0,
- GIT_ATTR_FILE__FROM_FILE = 1,
- GIT_ATTR_FILE__FROM_INDEX = 2,
-
- GIT_ATTR_FILE_NUM_SOURCES = 3
-} git_attr_file_source;
-
-extern const char *git_attr__true;
-extern const char *git_attr__false;
-extern const char *git_attr__unset;
-
-typedef struct {
- char *pattern;
- size_t length;
- char *containing_dir;
- size_t containing_dir_length;
- unsigned int flags;
-} git_attr_fnmatch;
-
-typedef struct {
- git_attr_fnmatch match;
- git_vector assigns; /* vector of <git_attr_assignment*> */
-} git_attr_rule;
-
-typedef struct {
- git_refcount unused;
- const char *name;
- uint32_t name_hash;
-} git_attr_name;
-
-typedef struct {
- git_refcount rc; /* for macros */
- char *name;
- uint32_t name_hash;
- const char *value;
-} git_attr_assignment;
-
-typedef struct git_attr_file_entry git_attr_file_entry;
-
-typedef struct {
- git_refcount rc;
- git_mutex lock;
- git_attr_file_entry *entry;
- git_attr_file_source source;
- git_vector rules; /* vector of <rule*> or <fnmatch*> */
- git_pool pool;
- unsigned int nonexistent:1;
- int session_key;
- union {
- git_oid oid;
- git_futils_filestamp stamp;
- } cache_data;
-} git_attr_file;
-
-struct git_attr_file_entry {
- git_attr_file *file[GIT_ATTR_FILE_NUM_SOURCES];
- const char *path; /* points into fullpath */
- char fullpath[GIT_FLEX_ARRAY];
-};
-
-typedef struct {
- git_buf full;
- char *path;
- char *basename;
- int is_dir;
-} git_attr_path;
-
-/* A git_attr_session can provide an "instance" of reading, to prevent cache
- * invalidation during a single operation instance (like checkout).
- */
-
-typedef struct {
- int key;
- unsigned int init_setup:1,
- init_sysdir:1;
- git_buf sysdir;
- git_buf tmp;
-} git_attr_session;
-
-extern int git_attr_session__init(git_attr_session *attr_session, git_repository *repo);
-extern void git_attr_session__free(git_attr_session *session);
-
-extern int git_attr_get_many_with_session(
- const char **values_out,
- git_repository *repo,
- git_attr_session *attr_session,
- uint32_t flags,
- const char *path,
- size_t num_attr,
- const char **names);
-
-typedef int (*git_attr_file_parser)(
- git_repository *repo,
- git_attr_file *file,
- const char *data);
-
-/*
- * git_attr_file API
- */
-
-int git_attr_file__new(
- git_attr_file **out,
- git_attr_file_entry *entry,
- git_attr_file_source source);
-
-void git_attr_file__free(git_attr_file *file);
-
-int git_attr_file__load(
- git_attr_file **out,
- git_repository *repo,
- git_attr_session *attr_session,
- git_attr_file_entry *ce,
- git_attr_file_source source,
- git_attr_file_parser parser);
-
-int git_attr_file__load_standalone(
- git_attr_file **out, const char *path);
-
-int git_attr_file__out_of_date(
- git_repository *repo, git_attr_session *session, git_attr_file *file);
-
-int git_attr_file__parse_buffer(
- git_repository *repo, git_attr_file *attrs, const char *data);
-
-int git_attr_file__clear_rules(
- git_attr_file *file, bool need_lock);
-
-int git_attr_file__lookup_one(
- git_attr_file *file,
- git_attr_path *path,
- const char *attr,
- const char **value);
-
-/* loop over rules in file from bottom to top */
-#define git_attr_file__foreach_matching_rule(file, path, iter, rule) \
- git_vector_rforeach(&(file)->rules, (iter), (rule)) \
- if (git_attr_rule__match((rule), (path)))
-
-uint32_t git_attr_file__name_hash(const char *name);
-
-
-/*
- * other utilities
- */
-
-extern int git_attr_fnmatch__parse(
- git_attr_fnmatch *spec,
- git_pool *pool,
- const char *source,
- const char **base);
-
-extern bool git_attr_fnmatch__match(
- git_attr_fnmatch *rule,
- git_attr_path *path);
-
-extern void git_attr_rule__free(git_attr_rule *rule);
-
-extern bool git_attr_rule__match(
- git_attr_rule *rule,
- git_attr_path *path);
-
-extern git_attr_assignment *git_attr_rule__lookup_assignment(
- git_attr_rule *rule, const char *name);
-
-typedef enum { GIT_DIR_FLAG_TRUE = 1, GIT_DIR_FLAG_FALSE = 0, GIT_DIR_FLAG_UNKNOWN = -1 } git_dir_flag;
-
-extern int git_attr_path__init(
- git_attr_path *info, const char *path, const char *base, git_dir_flag is_dir);
-
-extern void git_attr_path__free(git_attr_path *info);
-
-extern int git_attr_assignment__parse(
- git_repository *repo, /* needed to expand macros */
- git_pool *pool,
- git_vector *assigns,
- const char **scan);
-
-#endif
+++ /dev/null
-#include "common.h"
-#include "repository.h"
-#include "attr_file.h"
-#include "config.h"
-#include "sysdir.h"
-#include "ignore.h"
-
-GIT__USE_STRMAP
-
-GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache)
-{
- GIT_UNUSED(cache); /* avoid warning if threading is off */
-
- if (git_mutex_lock(&cache->lock) < 0) {
- giterr_set(GITERR_OS, "Unable to get attr cache lock");
- return -1;
- }
- return 0;
-}
-
-GIT_INLINE(void) attr_cache_unlock(git_attr_cache *cache)
-{
- GIT_UNUSED(cache); /* avoid warning if threading is off */
- git_mutex_unlock(&cache->lock);
-}
-
-GIT_INLINE(git_attr_file_entry *) attr_cache_lookup_entry(
- git_attr_cache *cache, const char *path)
-{
- khiter_t pos = git_strmap_lookup_index(cache->files, path);
-
- if (git_strmap_valid_index(cache->files, pos))
- return git_strmap_value_at(cache->files, pos);
- else
- return NULL;
-}
-
-int git_attr_cache__alloc_file_entry(
- git_attr_file_entry **out,
- const char *base,
- const char *path,
- git_pool *pool)
-{
- size_t baselen = 0, pathlen = strlen(path);
- size_t cachesize = sizeof(git_attr_file_entry) + pathlen + 1;
- git_attr_file_entry *ce;
-
- if (base != NULL && git_path_root(path) < 0) {
- baselen = strlen(base);
- cachesize += baselen;
-
- if (baselen && base[baselen - 1] != '/')
- cachesize++;
- }
-
- ce = git_pool_mallocz(pool, (uint32_t)cachesize);
- GITERR_CHECK_ALLOC(ce);
-
- if (baselen) {
- memcpy(ce->fullpath, base, baselen);
-
- if (base[baselen - 1] != '/')
- ce->fullpath[baselen++] = '/';
- }
- memcpy(&ce->fullpath[baselen], path, pathlen);
-
- ce->path = &ce->fullpath[baselen];
- *out = ce;
-
- return 0;
-}
-
-/* call with attrcache locked */
-static int attr_cache_make_entry(
- git_attr_file_entry **out, git_repository *repo, const char *path)
-{
- int error = 0;
- git_attr_cache *cache = git_repository_attr_cache(repo);
- git_attr_file_entry *entry = NULL;
-
- error = git_attr_cache__alloc_file_entry(
- &entry, git_repository_workdir(repo), path, &cache->pool);
-
- if (!error) {
- git_strmap_insert(cache->files, entry->path, entry, error);
- if (error > 0)
- error = 0;
- }
-
- *out = entry;
- return error;
-}
-
-/* insert entry or replace existing if we raced with another thread */
-static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file)
-{
- git_attr_file_entry *entry;
- git_attr_file *old;
-
- if (attr_cache_lock(cache) < 0)
- return -1;
-
- entry = attr_cache_lookup_entry(cache, file->entry->path);
-
- GIT_REFCOUNT_OWN(file, entry);
- GIT_REFCOUNT_INC(file);
-
- old = git__compare_and_swap(
- &entry->file[file->source], entry->file[file->source], file);
-
- if (old) {
- GIT_REFCOUNT_OWN(old, NULL);
- git_attr_file__free(old);
- }
-
- attr_cache_unlock(cache);
- return 0;
-}
-
-static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
-{
- int error = 0;
- git_attr_file_entry *entry;
-
- if (!file)
- return 0;
- if ((error = attr_cache_lock(cache)) < 0)
- return error;
-
- if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
- file = git__compare_and_swap(&entry->file[file->source], file, NULL);
-
- attr_cache_unlock(cache);
-
- if (file) {
- GIT_REFCOUNT_OWN(file, NULL);
- git_attr_file__free(file);
- }
-
- return error;
-}
-
-/* Look up cache entry and file.
- * - If entry is not present, create it while the cache is locked.
- * - If file is present, increment refcount before returning it, so the
- * cache can be unlocked and it won't go away.
- */
-static int attr_cache_lookup(
- git_attr_file **out_file,
- git_attr_file_entry **out_entry,
- git_repository *repo,
- git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename)
-{
- int error = 0;
- git_buf path = GIT_BUF_INIT;
- const char *wd = git_repository_workdir(repo), *relfile;
- git_attr_cache *cache = git_repository_attr_cache(repo);
- git_attr_file_entry *entry = NULL;
- git_attr_file *file = NULL;
-
- /* join base and path as needed */
- if (base != NULL && git_path_root(filename) < 0) {
- git_buf *p = attr_session ? &attr_session->tmp : &path;
-
- if (git_buf_joinpath(p, base, filename) < 0)
- return -1;
-
- filename = p->ptr;
- }
-
- relfile = filename;
- if (wd && !git__prefixcmp(relfile, wd))
- relfile += strlen(wd);
-
- /* check cache for existing entry */
- if ((error = attr_cache_lock(cache)) < 0)
- goto cleanup;
-
- entry = attr_cache_lookup_entry(cache, relfile);
- if (!entry)
- error = attr_cache_make_entry(&entry, repo, relfile);
- else if (entry->file[source] != NULL) {
- file = entry->file[source];
- GIT_REFCOUNT_INC(file);
- }
-
- attr_cache_unlock(cache);
-
-cleanup:
- *out_file = file;
- *out_entry = entry;
-
- git_buf_free(&path);
- return error;
-}
-
-int git_attr_cache__get(
- git_attr_file **out,
- git_repository *repo,
- git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename,
- git_attr_file_parser parser)
-{
- int error = 0;
- git_attr_cache *cache = git_repository_attr_cache(repo);
- git_attr_file_entry *entry = NULL;
- git_attr_file *file = NULL, *updated = NULL;
-
- if ((error = attr_cache_lookup(
- &file, &entry, repo, attr_session, source, base, filename)) < 0)
- return error;
-
- /* load file if we don't have one or if existing one is out of date */
- if (!file || (error = git_attr_file__out_of_date(repo, attr_session, file)) > 0)
- error = git_attr_file__load(&updated, repo, attr_session, entry, source, parser);
-
- /* if we loaded the file, insert into and/or update cache */
- if (updated) {
- if ((error = attr_cache_upsert(cache, updated)) < 0)
- git_attr_file__free(updated);
- else {
- git_attr_file__free(file); /* offset incref from lookup */
- file = updated;
- }
- }
-
- /* if file could not be loaded */
- if (error < 0) {
- /* remove existing entry */
- if (file) {
- attr_cache_remove(cache, file);
- git_attr_file__free(file); /* offset incref from lookup */
- file = NULL;
- }
- /* no error if file simply doesn't exist */
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
- }
-
- *out = file;
- return error;
-}
-
-bool git_attr_cache__is_cached(
- git_repository *repo,
- git_attr_file_source source,
- const char *filename)
-{
- git_attr_cache *cache = git_repository_attr_cache(repo);
- git_strmap *files;
- khiter_t pos;
- git_attr_file_entry *entry;
-
- if (!cache || !(files = cache->files))
- return false;
-
- pos = git_strmap_lookup_index(files, filename);
- if (!git_strmap_valid_index(files, pos))
- return false;
-
- entry = git_strmap_value_at(files, pos);
-
- return entry && (entry->file[source] != NULL);
-}
-
-
-static int attr_cache__lookup_path(
- char **out, git_config *cfg, const char *key, const char *fallback)
-{
- git_buf buf = GIT_BUF_INIT;
- int error;
- git_config_entry *entry = NULL;
-
- *out = NULL;
-
- if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0)
- return error;
-
- if (entry) {
- const char *cfgval = entry->value;
-
- /* expand leading ~/ as needed */
- if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' &&
- !git_sysdir_find_global_file(&buf, &cfgval[2]))
- *out = git_buf_detach(&buf);
- else if (cfgval)
- *out = git__strdup(cfgval);
- }
- else if (!git_sysdir_find_xdg_file(&buf, fallback))
- *out = git_buf_detach(&buf);
-
- git_config_entry_free(entry);
- git_buf_free(&buf);
-
- return error;
-}
-
-static void attr_cache__free(git_attr_cache *cache)
-{
- bool unlock;
-
- if (!cache)
- return;
-
- unlock = (git_mutex_lock(&cache->lock) == 0);
-
- if (cache->files != NULL) {
- git_attr_file_entry *entry;
- git_attr_file *file;
- int i;
-
- git_strmap_foreach_value(cache->files, entry, {
- for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) {
- if ((file = git__swap(entry->file[i], NULL)) != NULL) {
- GIT_REFCOUNT_OWN(file, NULL);
- git_attr_file__free(file);
- }
- }
- });
- git_strmap_free(cache->files);
- }
-
- if (cache->macros != NULL) {
- git_attr_rule *rule;
-
- git_strmap_foreach_value(cache->macros, rule, {
- git_attr_rule__free(rule);
- });
- git_strmap_free(cache->macros);
- }
-
- git_pool_clear(&cache->pool);
-
- git__free(cache->cfg_attr_file);
- cache->cfg_attr_file = NULL;
-
- git__free(cache->cfg_excl_file);
- cache->cfg_excl_file = NULL;
-
- if (unlock)
- git_mutex_unlock(&cache->lock);
- git_mutex_free(&cache->lock);
-
- git__free(cache);
-}
-
-int git_attr_cache__do_init(git_repository *repo)
-{
- int ret = 0;
- git_attr_cache *cache = git_repository_attr_cache(repo);
- git_config *cfg = NULL;
-
- if (cache)
- return 0;
-
- cache = git__calloc(1, sizeof(git_attr_cache));
- GITERR_CHECK_ALLOC(cache);
-
- /* set up lock */
- if (git_mutex_init(&cache->lock) < 0) {
- giterr_set(GITERR_OS, "Unable to initialize lock for attr cache");
- git__free(cache);
- return -1;
- }
-
- if ((ret = git_repository_config_snapshot(&cfg, repo)) < 0)
- goto cancel;
-
- /* cache config settings for attributes and ignores */
- ret = attr_cache__lookup_path(
- &cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
- if (ret < 0)
- goto cancel;
-
- ret = attr_cache__lookup_path(
- &cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG);
- if (ret < 0)
- goto cancel;
-
- /* allocate hashtable for attribute and ignore file contents,
- * hashtable for attribute macros, and string pool
- */
- if ((ret = git_strmap_alloc(&cache->files)) < 0 ||
- (ret = git_strmap_alloc(&cache->macros)) < 0)
- goto cancel;
-
- git_pool_init(&cache->pool, 1);
-
- cache = git__compare_and_swap(&repo->attrcache, NULL, cache);
- if (cache)
- goto cancel; /* raced with another thread, free this but no error */
-
- git_config_free(cfg);
-
- /* insert default macros */
- return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
-
-cancel:
- attr_cache__free(cache);
- git_config_free(cfg);
- return ret;
-}
-
-void git_attr_cache_flush(git_repository *repo)
-{
- git_attr_cache *cache;
-
- /* this could be done less expensively, but for now, we'll just free
- * the entire attrcache and let the next use reinitialize it...
- */
- if (repo && (cache = git__swap(repo->attrcache, NULL)) != NULL)
- attr_cache__free(cache);
-}
-
-int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
-{
- git_attr_cache *cache = git_repository_attr_cache(repo);
- git_strmap *macros = cache->macros;
- int error;
-
- /* TODO: generate warning log if (macro->assigns.length == 0) */
- if (macro->assigns.length == 0)
- return 0;
-
- if (git_mutex_lock(&cache->lock) < 0) {
- giterr_set(GITERR_OS, "Unable to get attr cache lock");
- error = -1;
- } else {
- git_strmap_insert(macros, macro->match.pattern, macro, error);
- git_mutex_unlock(&cache->lock);
- }
-
- return (error < 0) ? -1 : 0;
-}
-
-git_attr_rule *git_attr_cache__lookup_macro(
- git_repository *repo, const char *name)
-{
- git_strmap *macros = git_repository_attr_cache(repo)->macros;
- khiter_t pos;
-
- pos = git_strmap_lookup_index(macros, name);
-
- if (!git_strmap_valid_index(macros, pos))
- return NULL;
-
- return (git_attr_rule *)git_strmap_value_at(macros, pos);
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_attrcache_h__
-#define INCLUDE_attrcache_h__
-
-#include "attr_file.h"
-#include "strmap.h"
-
-#define GIT_ATTR_CONFIG "core.attributesfile"
-#define GIT_IGNORE_CONFIG "core.excludesfile"
-
-typedef struct {
- char *cfg_attr_file; /* cached value of core.attributesfile */
- char *cfg_excl_file; /* cached value of core.excludesfile */
- git_strmap *files; /* hash path to git_attr_cache_entry records */
- git_strmap *macros; /* hash name to vector<git_attr_assignment> */
- git_mutex lock;
- git_pool pool;
-} git_attr_cache;
-
-extern int git_attr_cache__do_init(git_repository *repo);
-
-#define git_attr_cache__init(REPO) \
- (git_repository_attr_cache(REPO) ? 0 : git_attr_cache__do_init(REPO))
-
-/* get file - loading and reload as needed */
-extern int git_attr_cache__get(
- git_attr_file **file,
- git_repository *repo,
- git_attr_session *attr_session,
- git_attr_file_source source,
- const char *base,
- const char *filename,
- git_attr_file_parser parser);
-
-extern bool git_attr_cache__is_cached(
- git_repository *repo,
- git_attr_file_source source,
- const char *path);
-
-extern int git_attr_cache__alloc_file_entry(
- git_attr_file_entry **out,
- const char *base,
- const char *path,
- git_pool *pool);
-
-extern int git_attr_cache__insert_macro(
- git_repository *repo, git_attr_rule *macro);
-
-extern git_attr_rule *git_attr_cache__lookup_macro(
- git_repository *repo, const char *name);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_bitvec_h__
-#define INCLUDE_bitvec_h__
-
-#include "common.h"
-
-/*
- * This is a silly little fixed length bit vector type that will store
- * vectors of 64 bits or less directly in the structure and allocate
- * memory for vectors longer than 64 bits. You can use the two versions
- * transparently through the API and avoid heap allocation completely when
- * using a short bit vector as a result.
- */
-typedef struct {
- size_t length;
- union {
- uint64_t *words;
- uint64_t bits;
- } u;
-} git_bitvec;
-
-GIT_INLINE(int) git_bitvec_init(git_bitvec *bv, size_t capacity)
-{
- memset(bv, 0x0, sizeof(*bv));
-
- if (capacity >= 64) {
- bv->length = (capacity / 64) + 1;
- bv->u.words = git__calloc(bv->length, sizeof(uint64_t));
- if (!bv->u.words)
- return -1;
- }
-
- return 0;
-}
-
-#define GIT_BITVEC_MASK(BIT) ((uint64_t)1 << (BIT % 64))
-#define GIT_BITVEC_WORD(BV, BIT) (BV->length ? &BV->u.words[BIT / 64] : &BV->u.bits)
-
-GIT_INLINE(void) git_bitvec_set(git_bitvec *bv, size_t bit, bool on)
-{
- uint64_t *word = GIT_BITVEC_WORD(bv, bit);
- uint64_t mask = GIT_BITVEC_MASK(bit);
-
- if (on)
- *word |= mask;
- else
- *word &= ~mask;
-}
-
-GIT_INLINE(bool) git_bitvec_get(git_bitvec *bv, size_t bit)
-{
- uint64_t *word = GIT_BITVEC_WORD(bv, bit);
- return (*word & GIT_BITVEC_MASK(bit)) != 0;
-}
-
-GIT_INLINE(void) git_bitvec_clear(git_bitvec *bv)
-{
- if (!bv->length)
- bv->u.bits = 0;
- else
- memset(bv->u.words, 0x0, bv->length * sizeof(uint64_t));
-}
-
-GIT_INLINE(void) git_bitvec_free(git_bitvec *bv)
-{
- if (bv->length)
- git__free(bv->u.words);
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "blame.h"
-#include "git2/commit.h"
-#include "git2/revparse.h"
-#include "git2/revwalk.h"
-#include "git2/tree.h"
-#include "git2/diff.h"
-#include "git2/blob.h"
-#include "git2/signature.h"
-#include "util.h"
-#include "repository.h"
-#include "blame_git.h"
-
-
-static int hunk_byfinalline_search_cmp(const void *key, const void *entry)
-{
- git_blame_hunk *hunk = (git_blame_hunk*)entry;
-
- size_t lineno = *(size_t*)key;
- size_t lines_in_hunk = hunk->lines_in_hunk;
- size_t final_start_line_number = hunk->final_start_line_number;
-
- if (lineno < final_start_line_number)
- return -1;
- if (lineno >= final_start_line_number + lines_in_hunk)
- return 1;
- return 0;
-}
-
-static int paths_cmp(const void *a, const void *b) { return git__strcmp((char*)a, (char*)b); }
-static int hunk_cmp(const void *_a, const void *_b)
-{
- git_blame_hunk *a = (git_blame_hunk*)_a,
- *b = (git_blame_hunk*)_b;
-
- return a->final_start_line_number - b->final_start_line_number;
-}
-
-static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line)
-{
- return line >= (hunk->final_start_line_number + hunk->lines_in_hunk - 1);
-}
-
-static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line)
-{
- return line <= hunk->final_start_line_number;
-}
-
-static git_blame_hunk* new_hunk(
- size_t start,
- size_t lines,
- size_t orig_start,
- const char *path)
-{
- git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk));
- if (!hunk) return NULL;
-
- hunk->lines_in_hunk = lines;
- hunk->final_start_line_number = start;
- hunk->orig_start_line_number = orig_start;
- hunk->orig_path = path ? git__strdup(path) : NULL;
-
- return hunk;
-}
-
-static git_blame_hunk* dup_hunk(git_blame_hunk *hunk)
-{
- git_blame_hunk *newhunk = new_hunk(
- hunk->final_start_line_number,
- hunk->lines_in_hunk,
- hunk->orig_start_line_number,
- hunk->orig_path);
-
- if (!newhunk)
- return NULL;
-
- git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id);
- git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id);
- newhunk->boundary = hunk->boundary;
- git_signature_dup(&newhunk->final_signature, hunk->final_signature);
- git_signature_dup(&newhunk->orig_signature, hunk->orig_signature);
- return newhunk;
-}
-
-static void free_hunk(git_blame_hunk *hunk)
-{
- git__free((void*)hunk->orig_path);
- git_signature_free(hunk->final_signature);
- git_signature_free(hunk->orig_signature);
- git__free(hunk);
-}
-
-/* Starting with the hunk that includes start_line, shift all following hunks'
- * final_start_line by shift_by lines */
-static void shift_hunks_by(git_vector *v, size_t start_line, int shift_by)
-{
- size_t i;
-
- if (!git_vector_bsearch2(&i, v, hunk_byfinalline_search_cmp, &start_line)) {
- for (; i < v->length; i++) {
- git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i];
- hunk->final_start_line_number += shift_by;
- }
- }
-}
-
-git_blame* git_blame__alloc(
- git_repository *repo,
- git_blame_options opts,
- const char *path)
-{
- git_blame *gbr = git__calloc(1, sizeof(git_blame));
- if (!gbr)
- return NULL;
-
- gbr->repository = repo;
- gbr->options = opts;
-
- if (git_vector_init(&gbr->hunks, 8, hunk_cmp) < 0 ||
- git_vector_init(&gbr->paths, 8, paths_cmp) < 0 ||
- (gbr->path = git__strdup(path)) == NULL ||
- git_vector_insert(&gbr->paths, git__strdup(path)) < 0)
- {
- git_blame_free(gbr);
- return NULL;
- }
-
- return gbr;
-}
-
-void git_blame_free(git_blame *blame)
-{
- size_t i;
- git_blame_hunk *hunk;
-
- if (!blame) return;
-
- git_vector_foreach(&blame->hunks, i, hunk)
- free_hunk(hunk);
- git_vector_free(&blame->hunks);
-
- git_vector_free_deep(&blame->paths);
-
- git_array_clear(blame->line_index);
-
- git__free(blame->path);
- git_blob_free(blame->final_blob);
- git__free(blame);
-}
-
-uint32_t git_blame_get_hunk_count(git_blame *blame)
-{
- assert(blame);
- return (uint32_t)blame->hunks.length;
-}
-
-const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t index)
-{
- assert(blame);
- return (git_blame_hunk*)git_vector_get(&blame->hunks, index);
-}
-
-const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno)
-{
- size_t i, new_lineno = lineno;
- assert(blame);
-
- if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) {
- return git_blame_get_hunk_byindex(blame, (uint32_t)i);
- }
-
- return NULL;
-}
-
-static int normalize_options(
- git_blame_options *out,
- const git_blame_options *in,
- git_repository *repo)
-{
- git_blame_options dummy = GIT_BLAME_OPTIONS_INIT;
- if (!in) in = &dummy;
-
- memcpy(out, in, sizeof(git_blame_options));
-
- /* No newest_commit => HEAD */
- if (git_oid_iszero(&out->newest_commit)) {
- if (git_reference_name_to_id(&out->newest_commit, repo, "HEAD") < 0) {
- return -1;
- }
- }
-
- /* min_line 0 really means 1 */
- if (!out->min_line) out->min_line = 1;
- /* max_line 0 really means N, but we don't know N yet */
-
- /* Fix up option implications */
- if (out->flags & GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES)
- out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
- if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES)
- out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
- if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES)
- out->flags |= GIT_BLAME_TRACK_COPIES_SAME_FILE;
-
- return 0;
-}
-
-static git_blame_hunk *split_hunk_in_vector(
- git_vector *vec,
- git_blame_hunk *hunk,
- size_t rel_line,
- bool return_new)
-{
- size_t new_line_count;
- git_blame_hunk *nh;
-
- /* Don't split if already at a boundary */
- if (rel_line <= 0 ||
- rel_line >= hunk->lines_in_hunk)
- {
- return hunk;
- }
-
- new_line_count = hunk->lines_in_hunk - rel_line;
- nh = new_hunk(hunk->final_start_line_number + rel_line, new_line_count,
- hunk->orig_start_line_number + rel_line, hunk->orig_path);
-
- if (!nh)
- return NULL;
-
- git_oid_cpy(&nh->final_commit_id, &hunk->final_commit_id);
- git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id);
-
- /* Adjust hunk that was split */
- hunk->lines_in_hunk -= new_line_count;
- git_vector_insert_sorted(vec, nh, NULL);
- {
- git_blame_hunk *ret = return_new ? nh : hunk;
- return ret;
- }
-}
-
-/*
- * Construct a list of char indices for where lines begin
- * Adapted from core git:
- * https://github.com/gitster/git/blob/be5c9fb9049ed470e7005f159bb923a5f4de1309/builtin/blame.c#L1760-L1789
- */
-static int index_blob_lines(git_blame *blame)
-{
- const char *buf = blame->final_buf;
- git_off_t len = blame->final_buf_size;
- int num = 0, incomplete = 0, bol = 1;
- size_t *i;
-
- if (len && buf[len-1] != '\n')
- incomplete++; /* incomplete line at the end */
- while (len--) {
- if (bol) {
- i = git_array_alloc(blame->line_index);
- GITERR_CHECK_ALLOC(i);
- *i = buf - blame->final_buf;
- bol = 0;
- }
- if (*buf++ == '\n') {
- num++;
- bol = 1;
- }
- }
- i = git_array_alloc(blame->line_index);
- GITERR_CHECK_ALLOC(i);
- *i = buf - blame->final_buf;
- blame->num_lines = num + incomplete;
- return blame->num_lines;
-}
-
-static git_blame_hunk* hunk_from_entry(git_blame__entry *e)
-{
- git_blame_hunk *h = new_hunk(
- e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path);
-
- if (!h)
- return NULL;
-
- git_oid_cpy(&h->final_commit_id, git_commit_id(e->suspect->commit));
- git_oid_cpy(&h->orig_commit_id, git_commit_id(e->suspect->commit));
- git_signature_dup(&h->final_signature, git_commit_author(e->suspect->commit));
- git_signature_dup(&h->orig_signature, git_commit_author(e->suspect->commit));
- h->boundary = e->is_boundary ? 1 : 0;
- return h;
-}
-
-static int load_blob(git_blame *blame)
-{
- int error;
-
- if (blame->final_blob) return 0;
-
- error = git_commit_lookup(&blame->final, blame->repository, &blame->options.newest_commit);
- if (error < 0)
- goto cleanup;
- error = git_object_lookup_bypath((git_object**)&blame->final_blob,
- (git_object*)blame->final, blame->path, GIT_OBJ_BLOB);
-
-cleanup:
- return error;
-}
-
-static int blame_internal(git_blame *blame)
-{
- int error;
- git_blame__entry *ent = NULL;
- git_blame__origin *o;
-
- if ((error = load_blob(blame)) < 0 ||
- (error = git_blame__get_origin(&o, blame, blame->final, blame->path)) < 0)
- goto cleanup;
- blame->final_buf = git_blob_rawcontent(blame->final_blob);
- blame->final_buf_size = git_blob_rawsize(blame->final_blob);
-
- ent = git__calloc(1, sizeof(git_blame__entry));
- GITERR_CHECK_ALLOC(ent);
-
- ent->num_lines = index_blob_lines(blame);
- ent->lno = blame->options.min_line - 1;
- ent->num_lines = ent->num_lines - blame->options.min_line + 1;
- if (blame->options.max_line > 0)
- ent->num_lines = blame->options.max_line - blame->options.min_line + 1;
- ent->s_lno = ent->lno;
- ent->suspect = o;
-
- blame->ent = ent;
-
- error = git_blame__like_git(blame, blame->options.flags);
-
-cleanup:
- for (ent = blame->ent; ent; ) {
- git_blame__entry *e = ent->next;
- git_blame_hunk *h = hunk_from_entry(ent);
-
- git_vector_insert(&blame->hunks, h);
-
- git_blame__free_entry(ent);
- ent = e;
- }
-
- return error;
-}
-
-/*******************************************************************************
- * File blaming
- ******************************************************************************/
-
-int git_blame_file(
- git_blame **out,
- git_repository *repo,
- const char *path,
- git_blame_options *options)
-{
- int error = -1;
- git_blame_options normOptions = GIT_BLAME_OPTIONS_INIT;
- git_blame *blame = NULL;
-
- assert(out && repo && path);
- if ((error = normalize_options(&normOptions, options, repo)) < 0)
- goto on_error;
-
- blame = git_blame__alloc(repo, normOptions, path);
- GITERR_CHECK_ALLOC(blame);
-
- if ((error = load_blob(blame)) < 0)
- goto on_error;
-
- if ((error = blame_internal(blame)) < 0)
- goto on_error;
-
- *out = blame;
- return 0;
-
-on_error:
- git_blame_free(blame);
- return error;
-}
-
-/*******************************************************************************
- * Buffer blaming
- *******************************************************************************/
-
-static bool hunk_is_bufferblame(git_blame_hunk *hunk)
-{
- return git_oid_iszero(&hunk->final_commit_id);
-}
-
-static int buffer_hunk_cb(
- const git_diff_delta *delta,
- const git_diff_hunk *hunk,
- void *payload)
-{
- git_blame *blame = (git_blame*)payload;
- uint32_t wedge_line;
-
- GIT_UNUSED(delta);
-
- wedge_line = (hunk->old_lines == 0) ? hunk->new_start : hunk->old_start;
- blame->current_diff_line = wedge_line;
-
- blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byline(blame, wedge_line);
- if (!blame->current_hunk) {
- /* Line added at the end of the file */
- blame->current_hunk = new_hunk(wedge_line, 0, wedge_line, blame->path);
- GITERR_CHECK_ALLOC(blame->current_hunk);
-
- git_vector_insert(&blame->hunks, blame->current_hunk);
- } else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){
- /* If this hunk doesn't start between existing hunks, split a hunk up so it does */
- blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk,
- wedge_line - blame->current_hunk->orig_start_line_number, true);
- GITERR_CHECK_ALLOC(blame->current_hunk);
- }
-
- return 0;
-}
-
-static int ptrs_equal_cmp(const void *a, const void *b) { return a<b ? -1 : a>b ? 1 : 0; }
-static int buffer_line_cb(
- const git_diff_delta *delta,
- const git_diff_hunk *hunk,
- const git_diff_line *line,
- void *payload)
-{
- git_blame *blame = (git_blame*)payload;
-
- GIT_UNUSED(delta);
- GIT_UNUSED(hunk);
- GIT_UNUSED(line);
-
- if (line->origin == GIT_DIFF_LINE_ADDITION) {
- if (hunk_is_bufferblame(blame->current_hunk) &&
- hunk_ends_at_or_before_line(blame->current_hunk, blame->current_diff_line)) {
- /* Append to the current buffer-blame hunk */
- blame->current_hunk->lines_in_hunk++;
- shift_hunks_by(&blame->hunks, blame->current_diff_line+1, 1);
- } else {
- /* Create a new buffer-blame hunk with this line */
- shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
- blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path);
- GITERR_CHECK_ALLOC(blame->current_hunk);
-
- git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);
- }
- blame->current_diff_line++;
- }
-
- if (line->origin == GIT_DIFF_LINE_DELETION) {
- /* Trim the line from the current hunk; remove it if it's now empty */
- size_t shift_base = blame->current_diff_line + blame->current_hunk->lines_in_hunk+1;
-
- if (--(blame->current_hunk->lines_in_hunk) == 0) {
- size_t i;
- shift_base--;
- if (!git_vector_search2(&i, &blame->hunks, ptrs_equal_cmp, blame->current_hunk)) {
- git_vector_remove(&blame->hunks, i);
- free_hunk(blame->current_hunk);
- blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byindex(blame, (uint32_t)i);
- }
- }
- shift_hunks_by(&blame->hunks, shift_base, -1);
- }
- return 0;
-}
-
-int git_blame_buffer(
- git_blame **out,
- git_blame *reference,
- const char *buffer,
- size_t buffer_len)
-{
- git_blame *blame;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- size_t i;
- git_blame_hunk *hunk;
-
- diffopts.context_lines = 0;
-
- assert(out && reference && buffer && buffer_len);
-
- blame = git_blame__alloc(reference->repository, reference->options, reference->path);
- GITERR_CHECK_ALLOC(blame);
-
- /* Duplicate all of the hunk structures in the reference blame */
- git_vector_foreach(&reference->hunks, i, hunk) {
- git_blame_hunk *h = dup_hunk(hunk);
- GITERR_CHECK_ALLOC(h);
-
- git_vector_insert(&blame->hunks, h);
- }
-
- /* Diff to the reference blob */
- git_diff_blob_to_buffer(reference->final_blob, blame->path,
- buffer, buffer_len, blame->path, &diffopts,
- NULL, NULL, buffer_hunk_cb, buffer_line_cb, blame);
-
- *out = blame;
- return 0;
-}
-
-int git_blame_init_options(git_blame_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_blame_options, GIT_BLAME_OPTIONS_INIT);
- return 0;
-}
+++ /dev/null
-#ifndef INCLUDE_blame_h__
-#define INCLUDE_blame_h__
-
-#include "git2/blame.h"
-#include "common.h"
-#include "vector.h"
-#include "diff.h"
-#include "array.h"
-#include "git2/oid.h"
-
-/*
- * One blob in a commit that is being suspected
- */
-typedef struct git_blame__origin {
- int refcnt;
- struct git_blame__origin *previous;
- git_commit *commit;
- git_blob *blob;
- char path[GIT_FLEX_ARRAY];
-} git_blame__origin;
-
-/*
- * Each group of lines is described by a git_blame__entry; it can be split
- * as we pass blame to the parents. They form a linked list in the
- * scoreboard structure, sorted by the target line number.
- */
-typedef struct git_blame__entry {
- struct git_blame__entry *prev;
- struct git_blame__entry *next;
-
- /* the first line of this group in the final image;
- * internally all line numbers are 0 based.
- */
- size_t lno;
-
- /* how many lines this group has */
- size_t num_lines;
-
- /* the commit that introduced this group into the final image */
- git_blame__origin *suspect;
-
- /* true if the suspect is truly guilty; false while we have not
- * checked if the group came from one of its parents.
- */
- bool guilty;
-
- /* true if the entry has been scanned for copies in the current parent
- */
- bool scanned;
-
- /* the line number of the first line of this group in the
- * suspect's file; internally all line numbers are 0 based.
- */
- size_t s_lno;
-
- /* how significant this entry is -- cached to avoid
- * scanning the lines over and over.
- */
- unsigned score;
-
- /* Whether this entry has been tracked to a boundary commit.
- */
- bool is_boundary;
-} git_blame__entry;
-
-struct git_blame {
- char *path;
- git_repository *repository;
- git_blame_options options;
-
- git_vector hunks;
- git_vector paths;
-
- git_blob *final_blob;
- git_array_t(size_t) line_index;
-
- size_t current_diff_line;
- git_blame_hunk *current_hunk;
-
- /* Scoreboard fields */
- git_commit *final;
- git_blame__entry *ent;
- int num_lines;
- const char *final_buf;
- git_off_t final_buf_size;
-};
-
-git_blame *git_blame__alloc(
- git_repository *repo,
- git_blame_options opts,
- const char *path);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "blame_git.h"
-#include "commit.h"
-#include "blob.h"
-#include "xdiff/xinclude.h"
-#include "diff_xdiff.h"
-
-/*
- * Origin is refcounted and usually we keep the blob contents to be
- * reused.
- */
-static git_blame__origin *origin_incref(git_blame__origin *o)
-{
- if (o)
- o->refcnt++;
- return o;
-}
-
-static void origin_decref(git_blame__origin *o)
-{
- if (o && --o->refcnt <= 0) {
- if (o->previous)
- origin_decref(o->previous);
- git_blob_free(o->blob);
- git_commit_free(o->commit);
- git__free(o);
- }
-}
-
-/* Given a commit and a path in it, create a new origin structure. */
-static int make_origin(git_blame__origin **out, git_commit *commit, const char *path)
-{
- git_blame__origin *o;
- git_object *blob;
- size_t path_len = strlen(path), alloc_len;
- int error = 0;
-
- if ((error = git_object_lookup_bypath(&blob, (git_object*)commit,
- path, GIT_OBJ_BLOB)) < 0)
- return error;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*o), path_len);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
- o = git__calloc(1, alloc_len);
- GITERR_CHECK_ALLOC(o);
-
- o->commit = commit;
- o->blob = (git_blob *) blob;
- o->refcnt = 1;
- strcpy(o->path, path);
-
- *out = o;
-
- return 0;
-}
-
-/* Locate an existing origin or create a new one. */
-int git_blame__get_origin(
- git_blame__origin **out,
- git_blame *blame,
- git_commit *commit,
- const char *path)
-{
- git_blame__entry *e;
-
- for (e = blame->ent; e; e = e->next) {
- if (e->suspect->commit == commit && !strcmp(e->suspect->path, path)) {
- *out = origin_incref(e->suspect);
- }
- }
- return make_origin(out, commit, path);
-}
-
-typedef struct blame_chunk_cb_data {
- git_blame *blame;
- git_blame__origin *target;
- git_blame__origin *parent;
- long tlno;
- long plno;
-}blame_chunk_cb_data;
-
-static bool same_suspect(git_blame__origin *a, git_blame__origin *b)
-{
- if (a == b)
- return true;
- if (git_oid_cmp(git_commit_id(a->commit), git_commit_id(b->commit)))
- return false;
- return 0 == strcmp(a->path, b->path);
-}
-
-/* find the line number of the last line the target is suspected for */
-static bool find_last_in_target(size_t *out, git_blame *blame, git_blame__origin *target)
-{
- git_blame__entry *e;
- size_t last_in_target = 0;
- bool found = false;
-
- *out = 0;
-
- for (e=blame->ent; e; e=e->next) {
- if (e->guilty || !same_suspect(e->suspect, target))
- continue;
- if (last_in_target < e->s_lno + e->num_lines) {
- found = true;
- last_in_target = e->s_lno + e->num_lines;
- }
- }
-
- *out = last_in_target;
- return found;
-}
-
-/*
- * It is known that lines between tlno to same came from parent, and e
- * has an overlap with that range. it also is known that parent's
- * line plno corresponds to e's line tlno.
- *
- * <---- e ----->
- * <------> (entirely within)
- * <------------> (extends past)
- * <------------> (starts before)
- * <------------------> (entirely encloses)
- *
- * Split e into potentially three parts; before this chunk, the chunk
- * to be blamed for the parent, and after that portion.
- */
-static void split_overlap(git_blame__entry *split, git_blame__entry *e,
- size_t tlno, size_t plno, size_t same, git_blame__origin *parent)
-{
- size_t chunk_end_lno;
-
- if (e->s_lno < tlno) {
- /* there is a pre-chunk part not blamed on the parent */
- split[0].suspect = origin_incref(e->suspect);
- split[0].lno = e->lno;
- split[0].s_lno = e->s_lno;
- split[0].num_lines = tlno - e->s_lno;
- split[1].lno = e->lno + tlno - e->s_lno;
- split[1].s_lno = plno;
- } else {
- split[1].lno = e->lno;
- split[1].s_lno = plno + (e->s_lno - tlno);
- }
-
- if (same < e->s_lno + e->num_lines) {
- /* there is a post-chunk part not blamed on parent */
- split[2].suspect = origin_incref(e->suspect);
- split[2].lno = e->lno + (same - e->s_lno);
- split[2].s_lno = e->s_lno + (same - e->s_lno);
- split[2].num_lines = e->s_lno + e->num_lines - same;
- chunk_end_lno = split[2].lno;
- } else {
- chunk_end_lno = e->lno + e->num_lines;
- }
- split[1].num_lines = chunk_end_lno - split[1].lno;
-
- /*
- * if it turns out there is nothing to blame the parent for, forget about
- * the splitting. !split[1].suspect signals this.
- */
- if (split[1].num_lines < 1)
- return;
- split[1].suspect = origin_incref(parent);
-}
-
-/*
- * Link in a new blame entry to the scoreboard. Entries that cover the same
- * line range have been removed from the scoreboard previously.
- */
-static void add_blame_entry(git_blame *blame, git_blame__entry *e)
-{
- git_blame__entry *ent, *prev = NULL;
-
- origin_incref(e->suspect);
-
- for (ent = blame->ent; ent && ent->lno < e->lno; ent = ent->next)
- prev = ent;
-
- /* prev, if not NULL, is the last one that is below e */
- e->prev = prev;
- if (prev) {
- e->next = prev->next;
- prev->next = e;
- } else {
- e->next = blame->ent;
- blame->ent = e;
- }
- if (e->next)
- e->next->prev = e;
-}
-
-/*
- * src typically is on-stack; we want to copy the information in it to
- * a malloced blame_entry that is already on the linked list of the scoreboard.
- * The origin of dst loses a refcnt while the origin of src gains one.
- */
-static void dup_entry(git_blame__entry *dst, git_blame__entry *src)
-{
- git_blame__entry *p, *n;
-
- p = dst->prev;
- n = dst->next;
- origin_incref(src->suspect);
- origin_decref(dst->suspect);
- memcpy(dst, src, sizeof(*src));
- dst->prev = p;
- dst->next = n;
- dst->score = 0;
-}
-
-/*
- * split_overlap() divided an existing blame e into up to three parts in split.
- * Adjust the linked list of blames in the scoreboard to reflect the split.
- */
-static void split_blame(git_blame *blame, git_blame__entry *split, git_blame__entry *e)
-{
- git_blame__entry *new_entry;
-
- if (split[0].suspect && split[2].suspect) {
- /* The first part (reuse storage for the existing entry e */
- dup_entry(e, &split[0]);
-
- /* The last part -- me */
- new_entry = git__malloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[2]), sizeof(git_blame__entry));
- add_blame_entry(blame, new_entry);
-
- /* ... and the middle part -- parent */
- new_entry = git__malloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[1]), sizeof(git_blame__entry));
- add_blame_entry(blame, new_entry);
- } else if (!split[0].suspect && !split[2].suspect) {
- /*
- * The parent covers the entire area; reuse storage for e and replace it
- * with the parent
- */
- dup_entry(e, &split[1]);
- } else if (split[0].suspect) {
- /* me and then parent */
- dup_entry(e, &split[0]);
- new_entry = git__malloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[1]), sizeof(git_blame__entry));
- add_blame_entry(blame, new_entry);
- } else {
- /* parent and then me */
- dup_entry(e, &split[1]);
- new_entry = git__malloc(sizeof(*new_entry));
- memcpy(new_entry, &(split[2]), sizeof(git_blame__entry));
- add_blame_entry(blame, new_entry);
- }
-}
-
-/*
- * After splitting the blame, the origins used by the on-stack blame_entry
- * should lose one refcnt each.
- */
-static void decref_split(git_blame__entry *split)
-{
- int i;
- for (i=0; i<3; i++)
- origin_decref(split[i].suspect);
-}
-
-/*
- * Helper for blame_chunk(). blame_entry e is known to overlap with the patch
- * hunk; split it and pass blame to the parent.
- */
-static void blame_overlap(
- git_blame *blame,
- git_blame__entry *e,
- size_t tlno,
- size_t plno,
- size_t same,
- git_blame__origin *parent)
-{
- git_blame__entry split[3] = {{0}};
-
- split_overlap(split, e, tlno, plno, same, parent);
- if (split[1].suspect)
- split_blame(blame, split, e);
- decref_split(split);
-}
-
-/*
- * Process one hunk from the patch between the current suspect for blame_entry
- * e and its parent. Find and split the overlap, and pass blame to the
- * overlapping part to the parent.
- */
-static void blame_chunk(
- git_blame *blame,
- size_t tlno,
- size_t plno,
- size_t same,
- git_blame__origin *target,
- git_blame__origin *parent)
-{
- git_blame__entry *e;
-
- for (e = blame->ent; e; e = e->next) {
- if (e->guilty || !same_suspect(e->suspect, target))
- continue;
- if (same <= e->s_lno)
- continue;
- if (tlno < e->s_lno + e->num_lines) {
- blame_overlap(blame, e, tlno, plno, same, parent);
- }
- }
-}
-
-static int my_emit(
- long start_a, long count_a,
- long start_b, long count_b,
- void *cb_data)
-{
- blame_chunk_cb_data *d = (blame_chunk_cb_data *)cb_data;
-
- blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent);
- d->plno = start_a + count_a;
- d->tlno = start_b + count_b;
-
- return 0;
-}
-
-static void trim_common_tail(mmfile_t *a, mmfile_t *b, long ctx)
-{
- const int blk = 1024;
- long trimmed = 0, recovered = 0;
- char *ap = a->ptr + a->size;
- char *bp = b->ptr + b->size;
- long smaller = (long)((a->size < b->size) ? a->size : b->size);
-
- if (ctx)
- return;
-
- while (blk + trimmed <= smaller && !memcmp(ap - blk, bp - blk, blk)) {
- trimmed += blk;
- ap -= blk;
- bp -= blk;
- }
-
- while (recovered < trimmed)
- if (ap[recovered++] == '\n')
- break;
- a->size -= trimmed - recovered;
- b->size -= trimmed - recovered;
-}
-
-static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data)
-{
- xpparam_t xpp = {0};
- xdemitconf_t xecfg = {0};
- xdemitcb_t ecb = {0};
-
- xecfg.hunk_func = my_emit;
- ecb.priv = cb_data;
-
- trim_common_tail(&file_a, &file_b, 0);
-
- if (file_a.size > GIT_XDIFF_MAX_SIZE ||
- file_b.size > GIT_XDIFF_MAX_SIZE) {
- giterr_set(GITERR_INVALID, "file too large to blame");
- return -1;
- }
-
- return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb);
-}
-
-static void fill_origin_blob(git_blame__origin *o, mmfile_t *file)
-{
- memset(file, 0, sizeof(*file));
- if (o->blob) {
- file->ptr = (char*)git_blob_rawcontent(o->blob);
- file->size = (size_t)git_blob_rawsize(o->blob);
- }
-}
-
-static int pass_blame_to_parent(
- git_blame *blame,
- git_blame__origin *target,
- git_blame__origin *parent)
-{
- size_t last_in_target;
- mmfile_t file_p, file_o;
- blame_chunk_cb_data d = { blame, target, parent, 0, 0 };
-
- if (!find_last_in_target(&last_in_target, blame, target))
- return 1; /* nothing remains for this target */
-
- fill_origin_blob(parent, &file_p);
- fill_origin_blob(target, &file_o);
-
- if (diff_hunks(file_p, file_o, &d) < 0)
- return -1;
-
- /* The reset (i.e. anything after tlno) are the same as the parent */
- blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent);
-
- return 0;
-}
-
-static int paths_on_dup(void **old, void *new)
-{
- GIT_UNUSED(old);
- git__free(new);
- return -1;
-}
-
-static git_blame__origin* find_origin(
- git_blame *blame,
- git_commit *parent,
- git_blame__origin *origin)
-{
- git_blame__origin *porigin = NULL;
- git_diff *difflist = NULL;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_tree *otree=NULL, *ptree=NULL;
-
- /* Get the trees from this commit and its parent */
- if (0 != git_commit_tree(&otree, origin->commit) ||
- 0 != git_commit_tree(&ptree, parent))
- goto cleanup;
-
- /* Configure the diff */
- diffopts.context_lines = 0;
- diffopts.flags = GIT_DIFF_SKIP_BINARY_CHECK;
-
- /* Check to see if files we're interested have changed */
- diffopts.pathspec.count = blame->paths.length;
- diffopts.pathspec.strings = (char**)blame->paths.contents;
- if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts))
- goto cleanup;
-
- if (!git_diff_num_deltas(difflist)) {
- /* No changes; copy data */
- git_blame__get_origin(&porigin, blame, parent, origin->path);
- } else {
- git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
- int i;
-
- /* Generate a full diff between the two trees */
- git_diff_free(difflist);
- diffopts.pathspec.count = 0;
- if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts))
- goto cleanup;
-
- /* Let diff find renames */
- findopts.flags = GIT_DIFF_FIND_RENAMES;
- if (0 != git_diff_find_similar(difflist, &findopts))
- goto cleanup;
-
- /* Find one that matches */
- for (i=0; i<(int)git_diff_num_deltas(difflist); i++) {
- const git_diff_delta *delta = git_diff_get_delta(difflist, i);
-
- if (!git_vector_bsearch(NULL, &blame->paths, delta->new_file.path))
- {
- git_vector_insert_sorted(&blame->paths, (void*)git__strdup(delta->old_file.path),
- paths_on_dup);
- make_origin(&porigin, parent, delta->old_file.path);
- }
- }
- }
-
-cleanup:
- git_diff_free(difflist);
- git_tree_free(otree);
- git_tree_free(ptree);
- return porigin;
-}
-
-/*
- * The blobs of origin and porigin exactly match, so everything origin is
- * suspected for can be blamed on the parent.
- */
-static void pass_whole_blame(git_blame *blame,
- git_blame__origin *origin, git_blame__origin *porigin)
-{
- git_blame__entry *e;
-
- if (!porigin->blob)
- git_object_lookup((git_object**)&porigin->blob, blame->repository,
- git_blob_id(origin->blob), GIT_OBJ_BLOB);
- for (e=blame->ent; e; e=e->next) {
- if (!same_suspect(e->suspect, origin))
- continue;
- origin_incref(porigin);
- origin_decref(e->suspect);
- e->suspect = porigin;
- }
-}
-
-static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
-{
- git_commit *commit = origin->commit;
- int i, num_parents;
- git_blame__origin *sg_buf[16];
- git_blame__origin *porigin, **sg_origin = sg_buf;
- int ret, error = 0;
-
- num_parents = git_commit_parentcount(commit);
- if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit))
- /* Stop at oldest specified commit */
- num_parents = 0;
- else if (opt & GIT_BLAME_FIRST_PARENT && num_parents > 1)
- /* Limit search to the first parent */
- num_parents = 1;
-
- if (!num_parents) {
- git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit));
- goto finish;
- }
- else if (num_parents < (int)ARRAY_SIZE(sg_buf))
- memset(sg_buf, 0, sizeof(sg_buf));
- else
- sg_origin = git__calloc(num_parents, sizeof(*sg_origin));
-
- for (i=0; i<num_parents; i++) {
- git_commit *p;
- int j, same;
-
- if (sg_origin[i])
- continue;
-
- if ((error = git_commit_parent(&p, origin->commit, i)) < 0)
- goto finish;
- porigin = find_origin(blame, p, origin);
-
- if (!porigin) {
- /*
- * We only have to decrement the parent's
- * reference count when no porigin has
- * been created, as otherwise the commit
- * is assigned to the created object.
- */
- git_commit_free(p);
- continue;
- }
- if (porigin->blob && origin->blob &&
- !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) {
- pass_whole_blame(blame, origin, porigin);
- origin_decref(porigin);
- goto finish;
- }
- for (j = same = 0; j<i; j++)
- if (sg_origin[j] &&
- !git_oid_cmp(git_blob_id(sg_origin[j]->blob), git_blob_id(porigin->blob))) {
- same = 1;
- break;
- }
- if (!same)
- sg_origin[i] = porigin;
- else
- origin_decref(porigin);
- }
-
- /* Standard blame */
- for (i=0; i<num_parents; i++) {
- git_blame__origin *porigin = sg_origin[i];
- if (!porigin)
- continue;
- if (!origin->previous) {
- origin_incref(porigin);
- origin->previous = porigin;
- }
-
- if ((ret = pass_blame_to_parent(blame, origin, porigin)) != 0) {
- if (ret < 0)
- error = -1;
-
- goto finish;
- }
- }
-
- /* TODO: optionally find moves in parents' files */
-
- /* TODO: optionally find copies in parents' files */
-
-finish:
- for (i=0; i<num_parents; i++)
- if (sg_origin[i])
- origin_decref(sg_origin[i]);
- if (sg_origin != sg_buf)
- git__free(sg_origin);
- return error;
-}
-
-/*
- * If two blame entries that are next to each other came from
- * contiguous lines in the same origin (i.e. <commit, path> pair),
- * merge them together.
- */
-static void coalesce(git_blame *blame)
-{
- git_blame__entry *ent, *next;
-
- for (ent=blame->ent; ent && (next = ent->next); ent = next) {
- if (same_suspect(ent->suspect, next->suspect) &&
- ent->guilty == next->guilty &&
- ent->s_lno + ent->num_lines == next->s_lno)
- {
- ent->num_lines += next->num_lines;
- ent->next = next->next;
- if (ent->next)
- ent->next->prev = ent;
- origin_decref(next->suspect);
- git__free(next);
- ent->score = 0;
- next = ent; /* again */
- }
- }
-}
-
-int git_blame__like_git(git_blame *blame, uint32_t opt)
-{
- while (true) {
- git_blame__entry *ent;
- git_blame__origin *suspect = NULL;
-
- /* Find a suspect to break down */
- for (ent = blame->ent; !suspect && ent; ent = ent->next)
- if (!ent->guilty)
- suspect = ent->suspect;
- if (!suspect)
- return 0; /* all done */
-
- /* We'll use this suspect later in the loop, so hold on to it for now. */
- origin_incref(suspect);
-
- if (pass_blame(blame, suspect, opt) < 0)
- return -1;
-
- /* Take responsibility for the remaining entries */
- for (ent = blame->ent; ent; ent = ent->next) {
- if (same_suspect(ent->suspect, suspect)) {
- ent->guilty = true;
- ent->is_boundary = !git_oid_cmp(
- git_commit_id(suspect->commit),
- &blame->options.oldest_commit);
- }
- }
- origin_decref(suspect);
- }
-
- coalesce(blame);
-
- return 0;
-}
-
-void git_blame__free_entry(git_blame__entry *ent)
-{
- if (!ent) return;
- origin_decref(ent->suspect);
- git__free(ent);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_blame_git__
-#define INCLUDE_blame_git__
-
-#include "blame.h"
-
-int git_blame__get_origin(
- git_blame__origin **out,
- git_blame *sb,
- git_commit *commit,
- const char *path);
-void git_blame__free_entry(git_blame__entry *ent);
-int git_blame__like_git(git_blame *sb, uint32_t flags);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/common.h"
-#include "git2/object.h"
-#include "git2/repository.h"
-#include "git2/odb_backend.h"
-
-#include "common.h"
-#include "filebuf.h"
-#include "blob.h"
-#include "filter.h"
-#include "buf_text.h"
-
-const void *git_blob_rawcontent(const git_blob *blob)
-{
- assert(blob);
- return git_odb_object_data(blob->odb_object);
-}
-
-git_off_t git_blob_rawsize(const git_blob *blob)
-{
- assert(blob);
- return (git_off_t)git_odb_object_size(blob->odb_object);
-}
-
-int git_blob__getbuf(git_buf *buffer, git_blob *blob)
-{
- return git_buf_set(
- buffer,
- git_odb_object_data(blob->odb_object),
- git_odb_object_size(blob->odb_object));
-}
-
-void git_blob__free(void *blob)
-{
- git_odb_object_free(((git_blob *)blob)->odb_object);
- git__free(blob);
-}
-
-int git_blob__parse(void *blob, git_odb_object *odb_obj)
-{
- assert(blob);
- git_cached_obj_incref((git_cached_obj *)odb_obj);
- ((git_blob *)blob)->odb_object = odb_obj;
- return 0;
-}
-
-int git_blob_create_frombuffer(
- git_oid *id, git_repository *repo, const void *buffer, size_t len)
-{
- int error;
- git_odb *odb;
- git_odb_stream *stream;
-
- assert(id && repo);
-
- if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
- (error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
- return error;
-
- if ((error = git_odb_stream_write(stream, buffer, len)) == 0)
- error = git_odb_stream_finalize_write(id, stream);
-
- git_odb_stream_free(stream);
- return error;
-}
-
-static int write_file_stream(
- git_oid *id, git_odb *odb, const char *path, git_off_t file_size)
-{
- int fd, error;
- char buffer[FILEIO_BUFSIZE];
- git_odb_stream *stream = NULL;
- ssize_t read_len = -1;
- git_off_t written = 0;
-
- if ((error = git_odb_open_wstream(
- &stream, odb, file_size, GIT_OBJ_BLOB)) < 0)
- return error;
-
- if ((fd = git_futils_open_ro(path)) < 0) {
- git_odb_stream_free(stream);
- return -1;
- }
-
- while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
- error = git_odb_stream_write(stream, buffer, read_len);
- written += read_len;
- }
-
- p_close(fd);
-
- if (written != file_size || read_len < 0) {
- giterr_set(GITERR_OS, "Failed to read file into stream");
- error = -1;
- }
-
- if (!error)
- error = git_odb_stream_finalize_write(id, stream);
-
- git_odb_stream_free(stream);
- return error;
-}
-
-static int write_file_filtered(
- git_oid *id,
- git_off_t *size,
- git_odb *odb,
- const char *full_path,
- git_filter_list *fl)
-{
- int error;
- git_buf tgt = GIT_BUF_INIT;
-
- error = git_filter_list_apply_to_file(&tgt, fl, NULL, full_path);
-
- /* Write the file to disk if it was properly filtered */
- if (!error) {
- *size = tgt.size;
-
- error = git_odb_write(id, odb, tgt.ptr, tgt.size, GIT_OBJ_BLOB);
- }
-
- git_buf_free(&tgt);
- return error;
-}
-
-static int write_symlink(
- git_oid *id, git_odb *odb, const char *path, size_t link_size)
-{
- char *link_data;
- ssize_t read_len;
- int error;
-
- link_data = git__malloc(link_size);
- GITERR_CHECK_ALLOC(link_data);
-
- read_len = p_readlink(path, link_data, link_size);
- if (read_len != (ssize_t)link_size) {
- giterr_set(GITERR_OS, "Failed to create blob. Can't read symlink '%s'", path);
- git__free(link_data);
- return -1;
- }
-
- error = git_odb_write(id, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
- git__free(link_data);
- return error;
-}
-
-int git_blob__create_from_paths(
- git_oid *id,
- struct stat *out_st,
- git_repository *repo,
- const char *content_path,
- const char *hint_path,
- mode_t hint_mode,
- bool try_load_filters)
-{
- int error;
- struct stat st;
- git_odb *odb = NULL;
- git_off_t size;
- mode_t mode;
- git_buf path = GIT_BUF_INIT;
-
- assert(hint_path || !try_load_filters);
-
- if (!content_path) {
- if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
- return GIT_EBAREREPO;
-
- if (git_buf_joinpath(
- &path, git_repository_workdir(repo), hint_path) < 0)
- return -1;
-
- content_path = path.ptr;
- }
-
- if ((error = git_path_lstat(content_path, &st)) < 0 ||
- (error = git_repository_odb(&odb, repo)) < 0)
- goto done;
-
- if (S_ISDIR(st.st_mode)) {
- giterr_set(GITERR_ODB, "cannot create blob from '%s'; it is a directory", content_path);
- error = GIT_EDIRECTORY;
- goto done;
- }
-
- if (out_st)
- memcpy(out_st, &st, sizeof(st));
-
- size = st.st_size;
- mode = hint_mode ? hint_mode : st.st_mode;
-
- if (S_ISLNK(mode)) {
- error = write_symlink(id, odb, content_path, (size_t)size);
- } else {
- git_filter_list *fl = NULL;
-
- if (try_load_filters)
- /* Load the filters for writing this file to the ODB */
- error = git_filter_list_load(
- &fl, repo, NULL, hint_path,
- GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT);
-
- if (error < 0)
- /* well, that didn't work */;
- else if (fl == NULL)
- /* No filters need to be applied to the document: we can stream
- * directly from disk */
- error = write_file_stream(id, odb, content_path, size);
- else {
- /* We need to apply one or more filters */
- error = write_file_filtered(id, &size, odb, content_path, fl);
-
- git_filter_list_free(fl);
- }
-
- /*
- * TODO: eventually support streaming filtered files, for files
- * which are bigger than a given threshold. This is not a priority
- * because applying a filter in streaming mode changes the final
- * size of the blob, and without knowing its final size, the blob
- * cannot be written in stream mode to the ODB.
- *
- * The plan is to do streaming writes to a tempfile on disk and then
- * opening streaming that file to the ODB, using
- * `write_file_stream`.
- *
- * CAREFULLY DESIGNED APIS YO
- */
- }
-
-done:
- git_odb_free(odb);
- git_buf_free(&path);
-
- return error;
-}
-
-int git_blob_create_fromworkdir(
- git_oid *id, git_repository *repo, const char *path)
-{
- return git_blob__create_from_paths(id, NULL, repo, NULL, path, 0, true);
-}
-
-int git_blob_create_fromdisk(
- git_oid *id, git_repository *repo, const char *path)
-{
- int error;
- git_buf full_path = GIT_BUF_INIT;
- const char *workdir, *hintpath;
-
- if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
- git_buf_free(&full_path);
- return error;
- }
-
- hintpath = git_buf_cstr(&full_path);
- workdir = git_repository_workdir(repo);
-
- if (workdir && !git__prefixcmp(hintpath, workdir))
- hintpath += strlen(workdir);
-
- error = git_blob__create_from_paths(
- id, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
-
- git_buf_free(&full_path);
- return error;
-}
-
-typedef struct {
- git_writestream parent;
- git_filebuf fbuf;
- git_repository *repo;
- char *hintpath;
-} blob_writestream;
-
-static int blob_writestream_close(git_writestream *_stream)
-{
- blob_writestream *stream = (blob_writestream *) _stream;
-
- git_filebuf_cleanup(&stream->fbuf);
- return 0;
-}
-
-static void blob_writestream_free(git_writestream *_stream)
-{
- blob_writestream *stream = (blob_writestream *) _stream;
-
- git_filebuf_cleanup(&stream->fbuf);
- git__free(stream->hintpath);
- git__free(stream);
-}
-
-static int blob_writestream_write(git_writestream *_stream, const char *buffer, size_t len)
-{
- blob_writestream *stream = (blob_writestream *) _stream;
-
- return git_filebuf_write(&stream->fbuf, buffer, len);
-}
-
-int git_blob_create_fromstream(git_writestream **out, git_repository *repo, const char *hintpath)
-{
- int error;
- git_buf path = GIT_BUF_INIT;
- blob_writestream *stream;
-
- assert(out && repo);
-
- stream = git__calloc(1, sizeof(blob_writestream));
- GITERR_CHECK_ALLOC(stream);
-
- if (hintpath) {
- stream->hintpath = git__strdup(hintpath);
- GITERR_CHECK_ALLOC(stream->hintpath);
- }
-
- stream->repo = repo;
- stream->parent.write = blob_writestream_write;
- stream->parent.close = blob_writestream_close;
- stream->parent.free = blob_writestream_free;
-
- if ((error = git_buf_joinpath(&path,
- git_repository_path(repo), GIT_OBJECTS_DIR "streamed")) < 0)
- goto cleanup;
-
- if ((error = git_filebuf_open_withsize(&stream->fbuf, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY,
- 0666, 2 * 1024 * 1024)) < 0)
- goto cleanup;
-
- *out = (git_writestream *) stream;
-
-cleanup:
- if (error < 0)
- blob_writestream_free((git_writestream *) stream);
-
- git_buf_free(&path);
- return error;
-}
-
-int git_blob_create_fromstream_commit(git_oid *out, git_writestream *_stream)
-{
- int error;
- blob_writestream *stream = (blob_writestream *) _stream;
-
- /*
- * We can make this more officient by avoiding writing to
- * disk, but for now let's re-use the helper functions we
- * have.
- */
- if ((error = git_filebuf_flush(&stream->fbuf)) < 0)
- goto cleanup;
-
- error = git_blob__create_from_paths(out, NULL, stream->repo, stream->fbuf.path_lock,
- stream->hintpath, 0, !!stream->hintpath);
-
-cleanup:
- blob_writestream_free(_stream);
- return error;
-
-}
-
-int git_blob_is_binary(const git_blob *blob)
-{
- git_buf content = GIT_BUF_INIT;
-
- assert(blob);
-
- git_buf_attach_notowned(&content, blob->odb_object->buffer,
- min(blob->odb_object->cached.size,
- GIT_FILTER_BYTES_TO_CHECK_NUL));
- return git_buf_text_is_binary(&content);
-}
-
-int git_blob_filtered_content(
- git_buf *out,
- git_blob *blob,
- const char *path,
- int check_for_binary_data)
-{
- int error = 0;
- git_filter_list *fl = NULL;
-
- assert(blob && path && out);
-
- git_buf_sanitize(out);
-
- if (check_for_binary_data && git_blob_is_binary(blob))
- return 0;
-
- if (!(error = git_filter_list_load(
- &fl, git_blob_owner(blob), blob, path,
- GIT_FILTER_TO_WORKTREE, GIT_FILTER_DEFAULT))) {
-
- error = git_filter_list_apply_to_blob(out, fl, blob);
-
- git_filter_list_free(fl);
- }
-
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_blob_h__
-#define INCLUDE_blob_h__
-
-#include "git2/blob.h"
-#include "repository.h"
-#include "odb.h"
-#include "fileops.h"
-
-struct git_blob {
- git_object object;
- git_odb_object *odb_object;
-};
-
-void git_blob__free(void *blob);
-int git_blob__parse(void *blob, git_odb_object *obj);
-int git_blob__getbuf(git_buf *buffer, git_blob *blob);
-
-extern int git_blob__create_from_paths(
- git_oid *out_oid,
- struct stat *out_st,
- git_repository *repo,
- const char *full_path,
- const char *hint_path,
- mode_t hint_mode,
- bool apply_filters);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "commit.h"
-#include "tag.h"
-#include "config.h"
-#include "refspec.h"
-#include "refs.h"
-#include "remote.h"
-#include "annotated_commit.h"
-
-#include "git2/branch.h"
-
-static int retrieve_branch_reference(
- git_reference **branch_reference_out,
- git_repository *repo,
- const char *branch_name,
- int is_remote)
-{
- git_reference *branch = NULL;
- int error = 0;
- char *prefix;
- git_buf ref_name = GIT_BUF_INIT;
-
- prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
-
- if ((error = git_buf_joinpath(&ref_name, prefix, branch_name)) < 0)
- /* OOM */;
- else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0)
- giterr_set(
- GITERR_REFERENCE, "Cannot locate %s branch '%s'",
- is_remote ? "remote-tracking" : "local", branch_name);
-
- *branch_reference_out = branch; /* will be NULL on error */
-
- git_buf_free(&ref_name);
- return error;
-}
-
-static int not_a_local_branch(const char *reference_name)
-{
- giterr_set(
- GITERR_INVALID,
- "Reference '%s' is not a local branch.", reference_name);
- return -1;
-}
-
-static int create_branch(
- git_reference **ref_out,
- git_repository *repository,
- const char *branch_name,
- const git_commit *commit,
- const char *from,
- int force)
-{
- int is_unmovable_head = 0;
- git_reference *branch = NULL;
- git_buf canonical_branch_name = GIT_BUF_INIT,
- log_message = GIT_BUF_INIT;
- int error = -1;
- int bare = git_repository_is_bare(repository);
-
- assert(branch_name && commit && ref_out);
- assert(git_object_owner((const git_object *)commit) == repository);
-
- if (force && !bare && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
- error = git_branch_is_head(branch);
- git_reference_free(branch);
- branch = NULL;
-
- if (error < 0)
- goto cleanup;
-
- is_unmovable_head = error;
- }
-
- if (is_unmovable_head && force) {
- giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is "
- "the current HEAD of the repository.", branch_name);
- error = -1;
- goto cleanup;
- }
-
- if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
- goto cleanup;
-
- if (git_buf_printf(&log_message, "branch: Created from %s", from) < 0)
- goto cleanup;
-
- error = git_reference_create(&branch, repository,
- git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force,
- git_buf_cstr(&log_message));
-
- if (!error)
- *ref_out = branch;
-
-cleanup:
- git_buf_free(&canonical_branch_name);
- git_buf_free(&log_message);
- return error;
-}
-
-int git_branch_create(
- git_reference **ref_out,
- git_repository *repository,
- const char *branch_name,
- const git_commit *commit,
- int force)
-{
- return create_branch(ref_out, repository, branch_name, commit, git_oid_tostr_s(git_commit_id(commit)), force);
-}
-
-int git_branch_create_from_annotated(
- git_reference **ref_out,
- git_repository *repository,
- const char *branch_name,
- const git_annotated_commit *commit,
- int force)
-{
- return create_branch(ref_out,
- repository, branch_name, commit->commit, commit->description, force);
-}
-
-int git_branch_delete(git_reference *branch)
-{
- int is_head;
- git_buf config_section = GIT_BUF_INIT;
- int error = -1;
-
- assert(branch);
-
- if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) {
- giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.",
- git_reference_name(branch));
- return GIT_ENOTFOUND;
- }
-
- if ((is_head = git_branch_is_head(branch)) < 0)
- return is_head;
-
- if (is_head) {
- giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is "
- "the current HEAD of the repository.", git_reference_name(branch));
- return -1;
- }
-
- if (git_buf_join(&config_section, '.', "branch",
- git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
- goto on_error;
-
- if (git_config_rename_section(
- git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0)
- goto on_error;
-
- error = git_reference_delete(branch);
-
-on_error:
- git_buf_free(&config_section);
- return error;
-}
-
-typedef struct {
- git_reference_iterator *iter;
- unsigned int flags;
-} branch_iter;
-
-int git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *_iter)
-{
- branch_iter *iter = (branch_iter *) _iter;
- git_reference *ref;
- int error;
-
- while ((error = git_reference_next(&ref, iter->iter)) == 0) {
- if ((iter->flags & GIT_BRANCH_LOCAL) &&
- !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR)) {
- *out = ref;
- *out_type = GIT_BRANCH_LOCAL;
-
- return 0;
- } else if ((iter->flags & GIT_BRANCH_REMOTE) &&
- !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
- *out = ref;
- *out_type = GIT_BRANCH_REMOTE;
-
- return 0;
- } else {
- git_reference_free(ref);
- }
- }
-
- return error;
-}
-
-int git_branch_iterator_new(
- git_branch_iterator **out,
- git_repository *repo,
- git_branch_t list_flags)
-{
- branch_iter *iter;
-
- iter = git__calloc(1, sizeof(branch_iter));
- GITERR_CHECK_ALLOC(iter);
-
- iter->flags = list_flags;
-
- if (git_reference_iterator_new(&iter->iter, repo) < 0) {
- git__free(iter);
- return -1;
- }
-
- *out = (git_branch_iterator *) iter;
-
- return 0;
-}
-
-void git_branch_iterator_free(git_branch_iterator *_iter)
-{
- branch_iter *iter = (branch_iter *) _iter;
-
- if (iter == NULL)
- return;
-
- git_reference_iterator_free(iter->iter);
- git__free(iter);
-}
-
-int git_branch_move(
- git_reference **out,
- git_reference *branch,
- const char *new_branch_name,
- int force)
-{
- git_buf new_reference_name = GIT_BUF_INIT,
- old_config_section = GIT_BUF_INIT,
- new_config_section = GIT_BUF_INIT,
- log_message = GIT_BUF_INIT;
- int error;
-
- assert(branch && new_branch_name);
-
- if (!git_reference_is_branch(branch))
- return not_a_local_branch(git_reference_name(branch));
-
- if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
- goto done;
-
- if ((error = git_buf_printf(&log_message, "branch: renamed %s to %s",
- git_reference_name(branch), git_buf_cstr(&new_reference_name))) < 0)
- goto done;
-
- /* first update ref then config so failure won't trash config */
-
- error = git_reference_rename(
- out, branch, git_buf_cstr(&new_reference_name), force,
- git_buf_cstr(&log_message));
- if (error < 0)
- goto done;
-
- git_buf_join(&old_config_section, '.', "branch",
- git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR));
- git_buf_join(&new_config_section, '.', "branch", new_branch_name);
-
- error = git_config_rename_section(
- git_reference_owner(branch),
- git_buf_cstr(&old_config_section),
- git_buf_cstr(&new_config_section));
-
-done:
- git_buf_free(&new_reference_name);
- git_buf_free(&old_config_section);
- git_buf_free(&new_config_section);
- git_buf_free(&log_message);
-
- return error;
-}
-
-int git_branch_lookup(
- git_reference **ref_out,
- git_repository *repo,
- const char *branch_name,
- git_branch_t branch_type)
-{
- assert(ref_out && repo && branch_name);
-
- return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
-}
-
-int git_branch_name(
- const char **out,
- const git_reference *ref)
-{
- const char *branch_name;
-
- assert(out && ref);
-
- branch_name = ref->name;
-
- if (git_reference_is_branch(ref)) {
- branch_name += strlen(GIT_REFS_HEADS_DIR);
- } else if (git_reference_is_remote(ref)) {
- branch_name += strlen(GIT_REFS_REMOTES_DIR);
- } else {
- giterr_set(GITERR_INVALID,
- "Reference '%s' is neither a local nor a remote branch.", ref->name);
- return -1;
- }
- *out = branch_name;
- return 0;
-}
-
-static int retrieve_upstream_configuration(
- git_buf *out,
- const git_config *config,
- const char *canonical_branch_name,
- const char *format)
-{
- git_buf buf = GIT_BUF_INIT;
- int error;
-
- if (git_buf_printf(&buf, format,
- canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0)
- return -1;
-
- error = git_config_get_string_buf(out, config, git_buf_cstr(&buf));
- git_buf_free(&buf);
- return error;
-}
-
-int git_branch_upstream_name(
- git_buf *out,
- git_repository *repo,
- const char *refname)
-{
- git_buf remote_name = GIT_BUF_INIT;
- git_buf merge_name = GIT_BUF_INIT;
- git_buf buf = GIT_BUF_INIT;
- int error = -1;
- git_remote *remote = NULL;
- const git_refspec *refspec;
- git_config *config;
-
- assert(out && refname);
-
- git_buf_sanitize(out);
-
- if (!git_reference__is_branch(refname))
- return not_a_local_branch(refname);
-
- if ((error = git_repository_config_snapshot(&config, repo)) < 0)
- return error;
-
- if ((error = retrieve_upstream_configuration(
- &remote_name, config, refname, "branch.%s.remote")) < 0)
- goto cleanup;
-
- if ((error = retrieve_upstream_configuration(
- &merge_name, config, refname, "branch.%s.merge")) < 0)
- goto cleanup;
-
- if (git_buf_len(&remote_name) == 0 || git_buf_len(&merge_name) == 0) {
- giterr_set(GITERR_REFERENCE,
- "branch '%s' does not have an upstream", refname);
- error = GIT_ENOTFOUND;
- goto cleanup;
- }
-
- if (strcmp(".", git_buf_cstr(&remote_name)) != 0) {
- if ((error = git_remote_lookup(&remote, repo, git_buf_cstr(&remote_name))) < 0)
- goto cleanup;
-
- refspec = git_remote__matching_refspec(remote, git_buf_cstr(&merge_name));
- if (!refspec) {
- error = GIT_ENOTFOUND;
- goto cleanup;
- }
-
- if (git_refspec_transform(&buf, refspec, git_buf_cstr(&merge_name)) < 0)
- goto cleanup;
- } else
- if (git_buf_set(&buf, git_buf_cstr(&merge_name), git_buf_len(&merge_name)) < 0)
- goto cleanup;
-
- error = git_buf_set(out, git_buf_cstr(&buf), git_buf_len(&buf));
-
-cleanup:
- git_config_free(config);
- git_remote_free(remote);
- git_buf_free(&remote_name);
- git_buf_free(&merge_name);
- git_buf_free(&buf);
- return error;
-}
-
-int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname)
-{
- int error;
- git_config *cfg;
-
- if (!git_reference__is_branch(refname))
- return not_a_local_branch(refname);
-
- if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
- return error;
-
- git_buf_sanitize(buf);
-
- if ((error = retrieve_upstream_configuration(buf, cfg, refname, "branch.%s.remote")) < 0)
- return error;
-
- if (git_buf_len(buf) == 0) {
- giterr_set(GITERR_REFERENCE, "branch '%s' does not have an upstream remote", refname);
- error = GIT_ENOTFOUND;
- git_buf_clear(buf);
- }
-
- return error;
-}
-
-int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname)
-{
- git_strarray remote_list = {0};
- size_t i;
- git_remote *remote;
- const git_refspec *fetchspec;
- int error = 0;
- char *remote_name = NULL;
-
- assert(buf && repo && refname);
-
- git_buf_sanitize(buf);
-
- /* Verify that this is a remote branch */
- if (!git_reference__is_remote(refname)) {
- giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.",
- refname);
- error = GIT_ERROR;
- goto cleanup;
- }
-
- /* Get the remotes */
- if ((error = git_remote_list(&remote_list, repo)) < 0)
- goto cleanup;
-
- /* Find matching remotes */
- for (i = 0; i < remote_list.count; i++) {
- if ((error = git_remote_lookup(&remote, repo, remote_list.strings[i])) < 0)
- continue;
-
- fetchspec = git_remote__matching_dst_refspec(remote, refname);
- if (fetchspec) {
- /* If we have not already set out yet, then set
- * it to the matching remote name. Otherwise
- * multiple remotes match this reference, and it
- * is ambiguous. */
- if (!remote_name) {
- remote_name = remote_list.strings[i];
- } else {
- git_remote_free(remote);
-
- giterr_set(GITERR_REFERENCE,
- "Reference '%s' is ambiguous", refname);
- error = GIT_EAMBIGUOUS;
- goto cleanup;
- }
- }
-
- git_remote_free(remote);
- }
-
- if (remote_name) {
- git_buf_clear(buf);
- error = git_buf_puts(buf, remote_name);
- } else {
- giterr_set(GITERR_REFERENCE,
- "Could not determine remote for '%s'", refname);
- error = GIT_ENOTFOUND;
- }
-
-cleanup:
- if (error < 0)
- git_buf_free(buf);
-
- git_strarray_free(&remote_list);
- return error;
-}
-
-int git_branch_upstream(
- git_reference **tracking_out,
- const git_reference *branch)
-{
- int error;
- git_buf tracking_name = GIT_BUF_INIT;
-
- if ((error = git_branch_upstream_name(&tracking_name,
- git_reference_owner(branch), git_reference_name(branch))) < 0)
- return error;
-
- error = git_reference_lookup(
- tracking_out,
- git_reference_owner(branch),
- git_buf_cstr(&tracking_name));
-
- git_buf_free(&tracking_name);
- return error;
-}
-
-static int unset_upstream(git_config *config, const char *shortname)
-{
- git_buf buf = GIT_BUF_INIT;
-
- if (git_buf_printf(&buf, "branch.%s.remote", shortname) < 0)
- return -1;
-
- if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
- goto on_error;
-
- git_buf_clear(&buf);
- if (git_buf_printf(&buf, "branch.%s.merge", shortname) < 0)
- goto on_error;
-
- if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
- goto on_error;
-
- git_buf_free(&buf);
- return 0;
-
-on_error:
- git_buf_free(&buf);
- return -1;
-}
-
-int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
-{
- git_buf key = GIT_BUF_INIT, value = GIT_BUF_INIT;
- git_reference *upstream;
- git_repository *repo;
- git_remote *remote = NULL;
- git_config *config;
- const char *name, *shortname;
- int local, error;
- const git_refspec *fetchspec;
-
- name = git_reference_name(branch);
- if (!git_reference__is_branch(name))
- return not_a_local_branch(name);
-
- if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
- return -1;
-
- shortname = name + strlen(GIT_REFS_HEADS_DIR);
-
- if (upstream_name == NULL)
- return unset_upstream(config, shortname);
-
- repo = git_reference_owner(branch);
-
- /* First we need to figure out whether it's a branch or remote-tracking */
- if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_LOCAL) == 0)
- local = 1;
- else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
- local = 0;
- else {
- giterr_set(GITERR_REFERENCE,
- "Cannot set upstream for branch '%s'", shortname);
- return GIT_ENOTFOUND;
- }
-
- /*
- * If it's local, the remote is "." and the branch name is
- * simply the refname. Otherwise we need to figure out what
- * the remote-tracking branch's name on the remote is and use
- * that.
- */
- if (local)
- error = git_buf_puts(&value, ".");
- else
- error = git_branch_remote_name(&value, repo, git_reference_name(upstream));
-
- if (error < 0)
- goto on_error;
-
- if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0)
- goto on_error;
-
- if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
- goto on_error;
-
- if (local) {
- git_buf_clear(&value);
- if (git_buf_puts(&value, git_reference_name(upstream)) < 0)
- goto on_error;
- } else {
- /* Get the remoe-tracking branch's refname in its repo */
- if (git_remote_lookup(&remote, repo, git_buf_cstr(&value)) < 0)
- goto on_error;
-
- fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
- git_buf_clear(&value);
- if (!fetchspec || git_refspec_rtransform(&value, fetchspec, git_reference_name(upstream)) < 0)
- goto on_error;
-
- git_remote_free(remote);
- remote = NULL;
- }
-
- git_buf_clear(&key);
- if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0)
- goto on_error;
-
- if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
- goto on_error;
-
- git_reference_free(upstream);
- git_buf_free(&key);
- git_buf_free(&value);
-
- return 0;
-
-on_error:
- git_reference_free(upstream);
- git_buf_free(&key);
- git_buf_free(&value);
- git_remote_free(remote);
-
- return -1;
-}
-
-int git_branch_is_head(
- const git_reference *branch)
-{
- git_reference *head;
- bool is_same = false;
- int error;
-
- assert(branch);
-
- if (!git_reference_is_branch(branch))
- return false;
-
- error = git_repository_head(&head, git_reference_owner(branch));
-
- if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
- return false;
-
- if (error < 0)
- return -1;
-
- is_same = strcmp(
- git_reference_name(branch),
- git_reference_name(head)) == 0;
-
- git_reference_free(head);
-
- return is_same;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_branch_h__
-#define INCLUDE_branch_h__
-
-#include "buffer.h"
-
-int git_branch_upstream__name(
- git_buf *tracking_name,
- git_repository *repo,
- const char *canonical_branch_name);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "buf_text.h"
-
-int git_buf_text_puts_escaped(
- git_buf *buf,
- const char *string,
- const char *esc_chars,
- const char *esc_with)
-{
- const char *scan;
- size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
-
- if (!string)
- return 0;
-
- for (scan = string; *scan; ) {
- /* count run of non-escaped characters */
- count = strcspn(scan, esc_chars);
- total += count;
- scan += count;
- /* count run of escaped characters */
- count = strspn(scan, esc_chars);
- total += count * (esc_len + 1);
- scan += count;
- }
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, total, 1);
- if (git_buf_grow_by(buf, alloclen) < 0)
- return -1;
-
- for (scan = string; *scan; ) {
- count = strcspn(scan, esc_chars);
-
- memmove(buf->ptr + buf->size, scan, count);
- scan += count;
- buf->size += count;
-
- for (count = strspn(scan, esc_chars); count > 0; --count) {
- /* copy escape sequence */
- memmove(buf->ptr + buf->size, esc_with, esc_len);
- buf->size += esc_len;
- /* copy character to be escaped */
- buf->ptr[buf->size] = *scan;
- buf->size++;
- scan++;
- }
- }
-
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-void git_buf_text_unescape(git_buf *buf)
-{
- buf->size = git__unescape(buf->ptr);
-}
-
-int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
-{
- const char *scan = src->ptr;
- const char *scan_end = src->ptr + src->size;
- const char *next = memchr(scan, '\r', src->size);
- size_t new_size;
- char *out;
-
- assert(tgt != src);
-
- if (!next)
- return git_buf_set(tgt, src->ptr, src->size);
-
- /* reduce reallocs while in the loop */
- GITERR_CHECK_ALLOC_ADD(&new_size, src->size, 1);
- if (git_buf_grow(tgt, new_size) < 0)
- return -1;
-
- out = tgt->ptr;
- tgt->size = 0;
-
- /* Find the next \r and copy whole chunk up to there to tgt */
- for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
- if (next > scan) {
- size_t copylen = (size_t)(next - scan);
- memcpy(out, scan, copylen);
- out += copylen;
- }
-
- /* Do not drop \r unless it is followed by \n */
- if (next + 1 == scan_end || next[1] != '\n')
- *out++ = '\r';
- }
-
- /* Copy remaining input into dest */
- if (scan < scan_end) {
- size_t remaining = (size_t)(scan_end - scan);
- memcpy(out, scan, remaining);
- out += remaining;
- }
-
- tgt->size = (size_t)(out - tgt->ptr);
- tgt->ptr[tgt->size] = '\0';
-
- return 0;
-}
-
-int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
-{
- const char *start = src->ptr;
- const char *end = start + src->size;
- const char *scan = start;
- const char *next = memchr(scan, '\n', src->size);
- size_t alloclen;
-
- assert(tgt != src);
-
- if (!next)
- return git_buf_set(tgt, src->ptr, src->size);
-
- /* attempt to reduce reallocs while in the loop */
- GITERR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
- if (git_buf_grow(tgt, alloclen) < 0)
- return -1;
- tgt->size = 0;
-
- for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
- size_t copylen = next - scan;
-
- /* if we find mixed line endings, carry on */
- if (copylen && next[-1] == '\r')
- copylen--;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, copylen, 3);
- if (git_buf_grow_by(tgt, alloclen) < 0)
- return -1;
-
- if (copylen) {
- memcpy(tgt->ptr + tgt->size, scan, copylen);
- tgt->size += copylen;
- }
-
- tgt->ptr[tgt->size++] = '\r';
- tgt->ptr[tgt->size++] = '\n';
- }
-
- tgt->ptr[tgt->size] = '\0';
- return git_buf_put(tgt, scan, end - scan);
-}
-
-int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strings)
-{
- size_t i;
- const char *str, *pfx;
-
- git_buf_clear(buf);
-
- if (!strings || !strings->count)
- return 0;
-
- /* initialize common prefix to first string */
- if (git_buf_sets(buf, strings->strings[0]) < 0)
- return -1;
-
- /* go through the rest of the strings, truncating to shared prefix */
- for (i = 1; i < strings->count; ++i) {
-
- for (str = strings->strings[i], pfx = buf->ptr;
- *str && *str == *pfx; str++, pfx++)
- /* scanning */;
-
- git_buf_truncate(buf, pfx - buf->ptr);
-
- if (!buf->size)
- break;
- }
-
- return 0;
-}
-
-bool git_buf_text_is_binary(const git_buf *buf)
-{
- const char *scan = buf->ptr, *end = buf->ptr + buf->size;
- git_bom_t bom;
- int printable = 0, nonprintable = 0;
-
- scan += git_buf_text_detect_bom(&bom, buf, 0);
-
- if (bom > GIT_BOM_UTF8)
- return 1;
-
- while (scan < end) {
- unsigned char c = *scan++;
-
- /* Printable characters are those above SPACE (0x1F) excluding DEL,
- * and including BS, ESC and FF.
- */
- if ((c > 0x1F && c != 127) || c == '\b' || c == '\033' || c == '\014')
- printable++;
- else if (c == '\0')
- return true;
- else if (!git__isspace(c))
- nonprintable++;
- }
-
- return ((printable >> 7) < nonprintable);
-}
-
-bool git_buf_text_contains_nul(const git_buf *buf)
-{
- return (memchr(buf->ptr, '\0', buf->size) != NULL);
-}
-
-int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf, size_t offset)
-{
- const char *ptr;
- size_t len;
-
- *bom = GIT_BOM_NONE;
- /* need at least 2 bytes after offset to look for any BOM */
- if (buf->size < offset + 2)
- return 0;
-
- ptr = buf->ptr + offset;
- len = buf->size - offset;
-
- switch (*ptr++) {
- case 0:
- if (len >= 4 && ptr[0] == 0 && ptr[1] == '\xFE' && ptr[2] == '\xFF') {
- *bom = GIT_BOM_UTF32_BE;
- return 4;
- }
- break;
- case '\xEF':
- if (len >= 3 && ptr[0] == '\xBB' && ptr[1] == '\xBF') {
- *bom = GIT_BOM_UTF8;
- return 3;
- }
- break;
- case '\xFE':
- if (*ptr == '\xFF') {
- *bom = GIT_BOM_UTF16_BE;
- return 2;
- }
- break;
- case '\xFF':
- if (*ptr != '\xFE')
- break;
- if (len >= 4 && ptr[1] == 0 && ptr[2] == 0) {
- *bom = GIT_BOM_UTF32_LE;
- return 4;
- } else {
- *bom = GIT_BOM_UTF16_LE;
- return 2;
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-bool git_buf_text_gather_stats(
- git_buf_text_stats *stats, const git_buf *buf, bool skip_bom)
-{
- const char *scan = buf->ptr, *end = buf->ptr + buf->size;
- int skip;
-
- memset(stats, 0, sizeof(*stats));
-
- /* BOM detection */
- skip = git_buf_text_detect_bom(&stats->bom, buf, 0);
- if (skip_bom)
- scan += skip;
-
- /* Ignore EOF character */
- if (buf->size > 0 && end[-1] == '\032')
- end--;
-
- /* Counting loop */
- while (scan < end) {
- unsigned char c = *scan++;
-
- if (c > 0x1F && c != 0x7F)
- stats->printable++;
- else switch (c) {
- case '\0':
- stats->nul++;
- stats->nonprintable++;
- break;
- case '\n':
- stats->lf++;
- break;
- case '\r':
- stats->cr++;
- if (scan < end && *scan == '\n')
- stats->crlf++;
- break;
- case '\t': case '\f': case '\v': case '\b': case 0x1b: /*ESC*/
- stats->printable++;
- break;
- default:
- stats->nonprintable++;
- break;
- }
- }
-
- return (stats->nul > 0 ||
- ((stats->printable >> 7) < stats->nonprintable));
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_buf_text_h__
-#define INCLUDE_buf_text_h__
-
-#include "buffer.h"
-
-typedef enum {
- GIT_BOM_NONE = 0,
- GIT_BOM_UTF8 = 1,
- GIT_BOM_UTF16_LE = 2,
- GIT_BOM_UTF16_BE = 3,
- GIT_BOM_UTF32_LE = 4,
- GIT_BOM_UTF32_BE = 5
-} git_bom_t;
-
-typedef struct {
- git_bom_t bom; /* BOM found at head of text */
- unsigned int nul, cr, lf, crlf; /* NUL, CR, LF and CRLF counts */
- unsigned int printable, nonprintable; /* These are just approximations! */
-} git_buf_text_stats;
-
-/**
- * Append string to buffer, prefixing each character from `esc_chars` with
- * `esc_with` string.
- *
- * @param buf Buffer to append data to
- * @param string String to escape and append
- * @param esc_chars Characters to be escaped
- * @param esc_with String to insert in from of each found character
- * @return 0 on success, <0 on failure (probably allocation problem)
- */
-extern int git_buf_text_puts_escaped(
- git_buf *buf,
- const char *string,
- const char *esc_chars,
- const char *esc_with);
-
-/**
- * Append string escaping characters that are regex special
- */
-GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
-{
- return git_buf_text_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\");
-}
-
-/**
- * Unescape all characters in a buffer in place
- *
- * I.e. remove backslashes
- */
-extern void git_buf_text_unescape(git_buf *buf);
-
-/**
- * Replace all \r\n with \n.
- *
- * @return 0 on success, -1 on memory error
- */
-extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
-
-/**
- * Replace all \n with \r\n. Does not modify existing \r\n.
- *
- * @return 0 on success, -1 on memory error
- */
-extern int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src);
-
-/**
- * Fill buffer with the common prefix of a array of strings
- *
- * Buffer will be set to empty if there is no common prefix
- */
-extern int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strs);
-
-/**
- * Check quickly if buffer looks like it contains binary data
- *
- * @param buf Buffer to check
- * @return true if buffer looks like non-text data
- */
-extern bool git_buf_text_is_binary(const git_buf *buf);
-
-/**
- * Check quickly if buffer contains a NUL byte
- *
- * @param buf Buffer to check
- * @return true if buffer contains a NUL byte
- */
-extern bool git_buf_text_contains_nul(const git_buf *buf);
-
-/**
- * Check if a buffer begins with a UTF BOM
- *
- * @param bom Set to the type of BOM detected or GIT_BOM_NONE
- * @param buf Buffer in which to check the first bytes for a BOM
- * @param offset Offset into buffer to look for BOM
- * @return Number of bytes of BOM data (or 0 if no BOM found)
- */
-extern int git_buf_text_detect_bom(
- git_bom_t *bom, const git_buf *buf, size_t offset);
-
-/**
- * Gather stats for a piece of text
- *
- * Fill the `stats` structure with counts of unreadable characters, carriage
- * returns, etc, so it can be used in heuristics. This automatically skips
- * a trailing EOF (\032 character). Also it will look for a BOM at the
- * start of the text and can be told to skip that as well.
- *
- * @param stats Structure to be filled in
- * @param buf Text to process
- * @param skip_bom Exclude leading BOM from stats if true
- * @return Does the buffer heuristically look like binary data
- */
-extern bool git_buf_text_gather_stats(
- git_buf_text_stats *stats, const git_buf *buf, bool skip_bom);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "buffer.h"
-#include "posix.h"
-#include "git2/buffer.h"
-#include "buf_text.h"
-#include <ctype.h>
-
-/* Used as default value for git_buf->ptr so that people can always
- * assume ptr is non-NULL and zero terminated even for new git_bufs.
- */
-char git_buf__initbuf[1];
-
-char git_buf__oom[1];
-
-#define ENSURE_SIZE(b, d) \
- if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
- return -1;
-
-
-void git_buf_init(git_buf *buf, size_t initial_size)
-{
- buf->asize = 0;
- buf->size = 0;
- buf->ptr = git_buf__initbuf;
-
- if (initial_size)
- git_buf_grow(buf, initial_size);
-}
-
-int git_buf_try_grow(
- git_buf *buf, size_t target_size, bool mark_oom)
-{
- char *new_ptr;
- size_t new_size;
-
- if (buf->ptr == git_buf__oom)
- return -1;
-
- if (buf->asize == 0 && buf->size != 0) {
- giterr_set(GITERR_INVALID, "cannot grow a borrowed buffer");
- return GIT_EINVALID;
- }
-
- if (!target_size)
- target_size = buf->size;
-
- if (target_size <= buf->asize)
- return 0;
-
- if (buf->asize == 0) {
- new_size = target_size;
- new_ptr = NULL;
- } else {
- new_size = buf->asize;
- new_ptr = buf->ptr;
- }
-
- /* grow the buffer size by 1.5, until it's big enough
- * to fit our target size */
- while (new_size < target_size)
- new_size = (new_size << 1) - (new_size >> 1);
-
- /* round allocation up to multiple of 8 */
- new_size = (new_size + 7) & ~7;
-
- if (new_size < buf->size) {
- if (mark_oom)
- buf->ptr = git_buf__oom;
-
- giterr_set_oom();
- return -1;
- }
-
- new_ptr = git__realloc(new_ptr, new_size);
-
- if (!new_ptr) {
- if (mark_oom) {
- if (buf->ptr && (buf->ptr != git_buf__initbuf))
- git__free(buf->ptr);
- buf->ptr = git_buf__oom;
- }
- return -1;
- }
-
- buf->asize = new_size;
- buf->ptr = new_ptr;
-
- /* truncate the existing buffer size if necessary */
- if (buf->size >= buf->asize)
- buf->size = buf->asize - 1;
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-int git_buf_grow(git_buf *buffer, size_t target_size)
-{
- return git_buf_try_grow(buffer, target_size, true);
-}
-
-int git_buf_grow_by(git_buf *buffer, size_t additional_size)
-{
- size_t newsize;
-
- if (GIT_ADD_SIZET_OVERFLOW(&newsize, buffer->size, additional_size)) {
- buffer->ptr = git_buf__oom;
- return -1;
- }
-
- return git_buf_try_grow(buffer, newsize, true);
-}
-
-void git_buf_free(git_buf *buf)
-{
- if (!buf) return;
-
- if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_buf__oom)
- git__free(buf->ptr);
-
- git_buf_init(buf, 0);
-}
-
-void git_buf_sanitize(git_buf *buf)
-{
- if (buf->ptr == NULL) {
- assert(buf->size == 0 && buf->asize == 0);
- buf->ptr = git_buf__initbuf;
- } else if (buf->asize > buf->size)
- buf->ptr[buf->size] = '\0';
-}
-
-void git_buf_clear(git_buf *buf)
-{
- buf->size = 0;
-
- if (!buf->ptr) {
- buf->ptr = git_buf__initbuf;
- buf->asize = 0;
- }
-
- if (buf->asize > 0)
- buf->ptr[0] = '\0';
-}
-
-int git_buf_set(git_buf *buf, const void *data, size_t len)
-{
- size_t alloclen;
-
- if (len == 0 || data == NULL) {
- git_buf_clear(buf);
- } else {
- if (data != buf->ptr) {
- GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
- ENSURE_SIZE(buf, alloclen);
- memmove(buf->ptr, data, len);
- }
-
- buf->size = len;
- if (buf->asize > buf->size)
- buf->ptr[buf->size] = '\0';
-
- }
- return 0;
-}
-
-int git_buf_is_binary(const git_buf *buf)
-{
- return git_buf_text_is_binary(buf);
-}
-
-int git_buf_contains_nul(const git_buf *buf)
-{
- return git_buf_text_contains_nul(buf);
-}
-
-int git_buf_sets(git_buf *buf, const char *string)
-{
- return git_buf_set(buf, string, string ? strlen(string) : 0);
-}
-
-int git_buf_putc(git_buf *buf, char c)
-{
- size_t new_size;
- GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, 2);
- ENSURE_SIZE(buf, new_size);
- buf->ptr[buf->size++] = c;
- buf->ptr[buf->size] = '\0';
- return 0;
-}
-
-int git_buf_putcn(git_buf *buf, char c, size_t len)
-{
- size_t new_size;
- GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
- GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
- memset(buf->ptr + buf->size, c, len);
- buf->size += len;
- buf->ptr[buf->size] = '\0';
- return 0;
-}
-
-int git_buf_put(git_buf *buf, const char *data, size_t len)
-{
- if (len) {
- size_t new_size;
-
- assert(data);
-
- GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
- GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
- memmove(buf->ptr + buf->size, data, len);
- buf->size += len;
- buf->ptr[buf->size] = '\0';
- }
- return 0;
-}
-
-int git_buf_puts(git_buf *buf, const char *string)
-{
- assert(string);
- return git_buf_put(buf, string, strlen(string));
-}
-
-static const char base64_encode[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
-{
- size_t extra = len % 3;
- uint8_t *write, a, b, c;
- const uint8_t *read = (const uint8_t *)data;
- size_t blocks = (len / 3) + !!extra, alloclen;
-
- GITERR_CHECK_ALLOC_ADD(&blocks, blocks, 1);
- GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 4);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
-
- ENSURE_SIZE(buf, alloclen);
- write = (uint8_t *)&buf->ptr[buf->size];
-
- /* convert each run of 3 bytes into 4 output bytes */
- for (len -= extra; len > 0; len -= 3) {
- a = *read++;
- b = *read++;
- c = *read++;
-
- *write++ = base64_encode[a >> 2];
- *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
- *write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
- *write++ = base64_encode[c & 0x3f];
- }
-
- if (extra > 0) {
- a = *read++;
- b = (extra > 1) ? *read++ : 0;
-
- *write++ = base64_encode[a >> 2];
- *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
- *write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
- *write++ = '=';
- }
-
- buf->size = ((char *)write) - buf->ptr;
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-/* The inverse of base64_encode */
-static const int8_t base64_decode[] = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
- -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-};
-
-int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
-{
- size_t i;
- int8_t a, b, c, d;
- size_t orig_size = buf->size, new_size;
-
- if (len % 4) {
- giterr_set(GITERR_INVALID, "invalid base64 input");
- return -1;
- }
-
- assert(len % 4 == 0);
- GITERR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
- GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
-
- for (i = 0; i < len; i += 4) {
- if ((a = base64_decode[(unsigned char)base64[i]]) < 0 ||
- (b = base64_decode[(unsigned char)base64[i+1]]) < 0 ||
- (c = base64_decode[(unsigned char)base64[i+2]]) < 0 ||
- (d = base64_decode[(unsigned char)base64[i+3]]) < 0) {
- buf->size = orig_size;
- buf->ptr[buf->size] = '\0';
-
- giterr_set(GITERR_INVALID, "invalid base64 input");
- return -1;
- }
-
- buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4);
- buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
- buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f);
- }
-
- buf->ptr[buf->size] = '\0';
- return 0;
-}
-
-static const char base85_encode[] =
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
-
-int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
-{
- size_t blocks = (len / 4) + !!(len % 4), alloclen;
-
- GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 5);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
-
- ENSURE_SIZE(buf, alloclen);
-
- while (len) {
- uint32_t acc = 0;
- char b85[5];
- int i;
-
- for (i = 24; i >= 0; i -= 8) {
- uint8_t ch = *data++;
- acc |= ch << i;
-
- if (--len == 0)
- break;
- }
-
- for (i = 4; i >= 0; i--) {
- int val = acc % 85;
- acc /= 85;
-
- b85[i] = base85_encode[val];
- }
-
- for (i = 0; i < 5; i++)
- buf->ptr[buf->size++] = b85[i];
- }
-
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-/* The inverse of base85_encode */
-static const int8_t base85_decode[] = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 63, -1, 64, 65, 66, 67, -1, 68, 69, 70, 71, -1, 72, -1, -1,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 73, 74, 75, 76, 77,
- 78, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
- 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, 79, 80,
- 81, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 82, 83, 84, 85, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
-};
-
-int git_buf_decode_base85(
- git_buf *buf,
- const char *base85,
- size_t base85_len,
- size_t output_len)
-{
- size_t orig_size = buf->size, new_size;
-
- if (base85_len % 5 ||
- output_len > base85_len * 4 / 5) {
- giterr_set(GITERR_INVALID, "invalid base85 input");
- return -1;
- }
-
- GITERR_CHECK_ALLOC_ADD(&new_size, output_len, buf->size);
- GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
-
- while (output_len) {
- unsigned acc = 0;
- int de, cnt = 4;
- unsigned char ch;
- do {
- ch = *base85++;
- de = base85_decode[ch];
- if (--de < 0)
- goto on_error;
-
- acc = acc * 85 + de;
- } while (--cnt);
- ch = *base85++;
- de = base85_decode[ch];
- if (--de < 0)
- goto on_error;
-
- /* Detect overflow. */
- if (0xffffffff / 85 < acc ||
- 0xffffffff - de < (acc *= 85))
- goto on_error;
-
- acc += de;
-
- cnt = (output_len < 4) ? output_len : 4;
- output_len -= cnt;
- do {
- acc = (acc << 8) | (acc >> 24);
- buf->ptr[buf->size++] = acc;
- } while (--cnt);
- }
-
- buf->ptr[buf->size] = 0;
-
- return 0;
-
-on_error:
- buf->size = orig_size;
- buf->ptr[buf->size] = '\0';
-
- giterr_set(GITERR_INVALID, "invalid base85 input");
- return -1;
-}
-
-int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
-{
- size_t expected_size, new_size;
- int len;
-
- GITERR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2);
- GITERR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size);
- ENSURE_SIZE(buf, expected_size);
-
- while (1) {
- va_list args;
- va_copy(args, ap);
-
- len = p_vsnprintf(
- buf->ptr + buf->size,
- buf->asize - buf->size,
- format, args
- );
-
- va_end(args);
-
- if (len < 0) {
- git__free(buf->ptr);
- buf->ptr = git_buf__oom;
- return -1;
- }
-
- if ((size_t)len + 1 <= buf->asize - buf->size) {
- buf->size += len;
- break;
- }
-
- GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
- GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
- ENSURE_SIZE(buf, new_size);
- }
-
- return 0;
-}
-
-int git_buf_printf(git_buf *buf, const char *format, ...)
-{
- int r;
- va_list ap;
-
- va_start(ap, format);
- r = git_buf_vprintf(buf, format, ap);
- va_end(ap);
-
- return r;
-}
-
-void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
-{
- size_t copylen;
-
- assert(data && datasize && buf);
-
- data[0] = '\0';
-
- if (buf->size == 0 || buf->asize <= 0)
- return;
-
- copylen = buf->size;
- if (copylen > datasize - 1)
- copylen = datasize - 1;
- memmove(data, buf->ptr, copylen);
- data[copylen] = '\0';
-}
-
-void git_buf_consume(git_buf *buf, const char *end)
-{
- if (end > buf->ptr && end <= buf->ptr + buf->size) {
- size_t consumed = end - buf->ptr;
- memmove(buf->ptr, end, buf->size - consumed);
- buf->size -= consumed;
- buf->ptr[buf->size] = '\0';
- }
-}
-
-void git_buf_truncate(git_buf *buf, size_t len)
-{
- if (len >= buf->size)
- return;
-
- buf->size = len;
- if (buf->size < buf->asize)
- buf->ptr[buf->size] = '\0';
-}
-
-void git_buf_shorten(git_buf *buf, size_t amount)
-{
- if (buf->size > amount)
- git_buf_truncate(buf, buf->size - amount);
- else
- git_buf_clear(buf);
-}
-
-void git_buf_rtruncate_at_char(git_buf *buf, char separator)
-{
- ssize_t idx = git_buf_rfind_next(buf, separator);
- git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
-}
-
-void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
-{
- git_buf t = *buf_a;
- *buf_a = *buf_b;
- *buf_b = t;
-}
-
-char *git_buf_detach(git_buf *buf)
-{
- char *data = buf->ptr;
-
- if (buf->asize == 0 || buf->ptr == git_buf__oom)
- return NULL;
-
- git_buf_init(buf, 0);
-
- return data;
-}
-
-void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
-{
- git_buf_free(buf);
-
- if (ptr) {
- buf->ptr = ptr;
- buf->size = strlen(ptr);
- if (asize)
- buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
- else /* pass 0 to fall back on strlen + 1 */
- buf->asize = buf->size + 1;
- } else {
- git_buf_grow(buf, asize);
- }
-}
-
-void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size)
-{
- if (git_buf_is_allocated(buf))
- git_buf_free(buf);
-
- if (!size) {
- git_buf_init(buf, 0);
- } else {
- buf->ptr = (char *)ptr;
- buf->asize = 0;
- buf->size = size;
- }
-}
-
-int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
-{
- va_list ap;
- int i;
- size_t total_size = 0, original_size = buf->size;
- char *out, *original = buf->ptr;
-
- if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
- ++total_size; /* space for initial separator */
-
- /* Make two passes to avoid multiple reallocation */
-
- va_start(ap, nbuf);
- for (i = 0; i < nbuf; ++i) {
- const char* segment;
- size_t segment_len;
-
- segment = va_arg(ap, const char *);
- if (!segment)
- continue;
-
- segment_len = strlen(segment);
-
- GITERR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len);
-
- if (segment_len == 0 || segment[segment_len - 1] != separator)
- GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
- }
- va_end(ap);
-
- /* expand buffer if needed */
- if (total_size == 0)
- return 0;
-
- GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
- if (git_buf_grow_by(buf, total_size) < 0)
- return -1;
-
- out = buf->ptr + buf->size;
-
- /* append separator to existing buf if needed */
- if (buf->size > 0 && out[-1] != separator)
- *out++ = separator;
-
- va_start(ap, nbuf);
- for (i = 0; i < nbuf; ++i) {
- const char* segment;
- size_t segment_len;
-
- segment = va_arg(ap, const char *);
- if (!segment)
- continue;
-
- /* deal with join that references buffer's original content */
- if (segment >= original && segment < original + original_size) {
- size_t offset = (segment - original);
- segment = buf->ptr + offset;
- segment_len = original_size - offset;
- } else {
- segment_len = strlen(segment);
- }
-
- /* skip leading separators */
- if (out > buf->ptr && out[-1] == separator)
- while (segment_len > 0 && *segment == separator) {
- segment++;
- segment_len--;
- }
-
- /* copy over next buffer */
- if (segment_len > 0) {
- memmove(out, segment, segment_len);
- out += segment_len;
- }
-
- /* append trailing separator (except for last item) */
- if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
- *out++ = separator;
- }
- va_end(ap);
-
- /* set size based on num characters actually written */
- buf->size = out - buf->ptr;
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-int git_buf_join(
- git_buf *buf,
- char separator,
- const char *str_a,
- const char *str_b)
-{
- size_t strlen_a = str_a ? strlen(str_a) : 0;
- size_t strlen_b = strlen(str_b);
- size_t alloc_len;
- int need_sep = 0;
- ssize_t offset_a = -1;
-
- /* not safe to have str_b point internally to the buffer */
- assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
-
- /* figure out if we need to insert a separator */
- if (separator && strlen_a) {
- while (*str_b == separator) { str_b++; strlen_b--; }
- if (str_a[strlen_a - 1] != separator)
- need_sep = 1;
- }
-
- /* str_a could be part of the buffer */
- if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
- offset_a = str_a - buf->ptr;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
- if (git_buf_grow(buf, alloc_len) < 0)
- return -1;
- assert(buf->ptr);
-
- /* fix up internal pointers */
- if (offset_a >= 0)
- str_a = buf->ptr + offset_a;
-
- /* do the actual copying */
- if (offset_a != 0 && str_a)
- memmove(buf->ptr, str_a, strlen_a);
- if (need_sep)
- buf->ptr[strlen_a] = separator;
- memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
-
- buf->size = strlen_a + strlen_b + need_sep;
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-int git_buf_join3(
- git_buf *buf,
- char separator,
- const char *str_a,
- const char *str_b,
- const char *str_c)
-{
- size_t len_a = strlen(str_a),
- len_b = strlen(str_b),
- len_c = strlen(str_c),
- len_total;
- int sep_a = 0, sep_b = 0;
- char *tgt;
-
- /* for this function, disallow pointers into the existing buffer */
- assert(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
- assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
- assert(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
-
- if (separator) {
- if (len_a > 0) {
- while (*str_b == separator) { str_b++; len_b--; }
- sep_a = (str_a[len_a - 1] != separator);
- }
- if (len_a > 0 || len_b > 0)
- while (*str_c == separator) { str_c++; len_c--; }
- if (len_b > 0)
- sep_b = (str_b[len_b - 1] != separator);
- }
-
- GITERR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a);
- GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_b);
- GITERR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
- GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
- GITERR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
- if (git_buf_grow(buf, len_total) < 0)
- return -1;
-
- tgt = buf->ptr;
-
- if (len_a) {
- memcpy(tgt, str_a, len_a);
- tgt += len_a;
- }
- if (sep_a)
- *tgt++ = separator;
- if (len_b) {
- memcpy(tgt, str_b, len_b);
- tgt += len_b;
- }
- if (sep_b)
- *tgt++ = separator;
- if (len_c)
- memcpy(tgt, str_c, len_c);
-
- buf->size = len_a + sep_a + len_b + sep_b + len_c;
- buf->ptr[buf->size] = '\0';
-
- return 0;
-}
-
-void git_buf_rtrim(git_buf *buf)
-{
- while (buf->size > 0) {
- if (!git__isspace(buf->ptr[buf->size - 1]))
- break;
-
- buf->size--;
- }
-
- if (buf->asize > buf->size)
- buf->ptr[buf->size] = '\0';
-}
-
-int git_buf_cmp(const git_buf *a, const git_buf *b)
-{
- int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
- return (result != 0) ? result :
- (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
-}
-
-int git_buf_splice(
- git_buf *buf,
- size_t where,
- size_t nb_to_remove,
- const char *data,
- size_t nb_to_insert)
-{
- char *splice_loc;
- size_t new_size, alloc_size;
-
- assert(buf && where <= buf->size && nb_to_remove <= buf->size - where);
-
- splice_loc = buf->ptr + where;
-
- /* Ported from git.git
- * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
- */
- GITERR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert);
- GITERR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1);
- ENSURE_SIZE(buf, alloc_size);
-
- memmove(splice_loc + nb_to_insert,
- splice_loc + nb_to_remove,
- buf->size - where - nb_to_remove);
-
- memcpy(splice_loc, data, nb_to_insert);
-
- buf->size = new_size;
- buf->ptr[buf->size] = '\0';
- return 0;
-}
-
-/* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
-int git_buf_quote(git_buf *buf)
-{
- const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
- git_buf quoted = GIT_BUF_INIT;
- size_t i = 0;
- bool quote = false;
- int error = 0;
-
- /* walk to the first char that needs quoting */
- if (buf->size && buf->ptr[0] == '!')
- quote = true;
-
- for (i = 0; !quote && i < buf->size; i++) {
- if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' ||
- buf->ptr[i] < ' ' || buf->ptr[i] > '~') {
- quote = true;
- break;
- }
- }
-
- if (!quote)
- goto done;
-
- git_buf_putc("ed, '"');
- git_buf_put("ed, buf->ptr, i);
-
- for (; i < buf->size; i++) {
- /* whitespace - use the map above, which is ordered by ascii value */
- if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') {
- git_buf_putc("ed, '\\');
- git_buf_putc("ed, whitespace[buf->ptr[i] - '\a']);
- }
-
- /* double quote and backslash must be escaped */
- else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
- git_buf_putc("ed, '\\');
- git_buf_putc("ed, buf->ptr[i]);
- }
-
- /* escape anything unprintable as octal */
- else if (buf->ptr[i] != ' ' &&
- (buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
- git_buf_printf("ed, "\\%03o", (unsigned char)buf->ptr[i]);
- }
-
- /* yay, printable! */
- else {
- git_buf_putc("ed, buf->ptr[i]);
- }
- }
-
- git_buf_putc("ed, '"');
-
- if (git_buf_oom("ed)) {
- error = -1;
- goto done;
- }
-
- git_buf_swap("ed, buf);
-
-done:
- git_buf_free("ed);
- return error;
-}
-
-/* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
-int git_buf_unquote(git_buf *buf)
-{
- size_t i, j;
- char ch;
-
- git_buf_rtrim(buf);
-
- if (buf->size < 2 || buf->ptr[0] != '"' || buf->ptr[buf->size-1] != '"')
- goto invalid;
-
- for (i = 0, j = 1; j < buf->size-1; i++, j++) {
- ch = buf->ptr[j];
-
- if (ch == '\\') {
- if (j == buf->size-2)
- goto invalid;
-
- ch = buf->ptr[++j];
-
- switch (ch) {
- /* \" or \\ simply copy the char in */
- case '"': case '\\':
- break;
-
- /* add the appropriate escaped char */
- case 'a': ch = '\a'; break;
- case 'b': ch = '\b'; break;
- case 'f': ch = '\f'; break;
- case 'n': ch = '\n'; break;
- case 'r': ch = '\r'; break;
- case 't': ch = '\t'; break;
- case 'v': ch = '\v'; break;
-
- /* \xyz digits convert to the char*/
- case '0': case '1': case '2': case '3':
- if (j == buf->size-3) {
- giterr_set(GITERR_INVALID,
- "Truncated quoted character \\%c", ch);
- return -1;
- }
-
- if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' ||
- buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') {
- giterr_set(GITERR_INVALID,
- "Truncated quoted character \\%c%c%c",
- buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]);
- return -1;
- }
-
- ch = ((buf->ptr[j] - '0') << 6) |
- ((buf->ptr[j+1] - '0') << 3) |
- (buf->ptr[j+2] - '0');
- j += 2;
- break;
-
- default:
- giterr_set(GITERR_INVALID, "Invalid quoted character \\%c", ch);
- return -1;
- }
- }
-
- buf->ptr[i] = ch;
- }
-
- buf->ptr[i] = '\0';
- buf->size = i;
-
- return 0;
-
-invalid:
- giterr_set(GITERR_INVALID, "Invalid quoted line");
- return -1;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_buffer_h__
-#define INCLUDE_buffer_h__
-
-#include "common.h"
-#include "git2/strarray.h"
-#include "git2/buffer.h"
-
-/* typedef struct {
- * char *ptr;
- * size_t asize, size;
- * } git_buf;
- */
-
-extern char git_buf__initbuf[];
-extern char git_buf__oom[];
-
-/* Use to initialize buffer structure when git_buf is on stack */
-#define GIT_BUF_INIT { git_buf__initbuf, 0, 0 }
-
-GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf)
-{
- return (buf->ptr != NULL && buf->asize > 0);
-}
-
-/**
- * Initialize a git_buf structure.
- *
- * For the cases where GIT_BUF_INIT cannot be used to do static
- * initialization.
- */
-extern void git_buf_init(git_buf *buf, size_t initial_size);
-
-/**
- * Resize the buffer allocation to make more space.
- *
- * This will attempt to grow the buffer to accommodate the additional size.
- * It is similar to `git_buf_grow`, but performs the new size calculation,
- * checking for overflow.
- *
- * Like `git_buf_grow`, if this is a user-supplied buffer, this will allocate
- * a new buffer.
- */
-extern int git_buf_grow_by(git_buf *buffer, size_t additional_size);
-
-/**
- * Attempt to grow the buffer to hold at least `target_size` bytes.
- *
- * If the allocation fails, this will return an error. If `mark_oom` is true,
- * this will mark the buffer as invalid for future operations; if false,
- * existing buffer content will be preserved, but calling code must handle
- * that buffer was not expanded. If `preserve_external` is true, then any
- * existing data pointed to be `ptr` even if `asize` is zero will be copied
- * into the newly allocated buffer.
- */
-extern int git_buf_try_grow(
- git_buf *buf, size_t target_size, bool mark_oom);
-
-/**
- * Sanitizes git_buf structures provided from user input. Users of the
- * library, when providing git_buf's, may wish to provide a NULL ptr for
- * ease of handling. The buffer routines, however, expect a non-NULL ptr
- * always. This helper method simply handles NULL input, converting to a
- * git_buf__initbuf.
- */
-extern void git_buf_sanitize(git_buf *buf);
-
-extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
-extern char *git_buf_detach(git_buf *buf);
-extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
-
-/* Populates a `git_buf` where the contents are not "owned" by the
- * buffer, and calls to `git_buf_free` will not free the given buf.
- */
-extern void git_buf_attach_notowned(
- git_buf *buf, const char *ptr, size_t size);
-
-/**
- * Test if there have been any reallocation failures with this git_buf.
- *
- * Any function that writes to a git_buf can fail due to memory allocation
- * issues. If one fails, the git_buf will be marked with an OOM error and
- * further calls to modify the buffer will fail. Check git_buf_oom() at the
- * end of your sequence and it will be true if you ran out of memory at any
- * point with that buffer.
- *
- * @return false if no error, true if allocation error
- */
-GIT_INLINE(bool) git_buf_oom(const git_buf *buf)
-{
- return (buf->ptr == git_buf__oom);
-}
-
-/*
- * Functions below that return int value error codes will return 0 on
- * success or -1 on failure (which generally means an allocation failed).
- * Using a git_buf where the allocation has failed with result in -1 from
- * all further calls using that buffer. As a result, you can ignore the
- * return code of these functions and call them in a series then just call
- * git_buf_oom at the end.
- */
-int git_buf_sets(git_buf *buf, const char *string);
-int git_buf_putc(git_buf *buf, char c);
-int git_buf_putcn(git_buf *buf, char c, size_t len);
-int git_buf_put(git_buf *buf, const char *data, size_t len);
-int git_buf_puts(git_buf *buf, const char *string);
-int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
-int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);
-void git_buf_clear(git_buf *buf);
-void git_buf_consume(git_buf *buf, const char *end);
-void git_buf_truncate(git_buf *buf, size_t len);
-void git_buf_shorten(git_buf *buf, size_t amount);
-void git_buf_rtruncate_at_char(git_buf *path, char separator);
-
-/** General join with separator */
-int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
-/** Fast join of two strings - first may legally point into `buf` data */
-int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
-/** Fast join of three strings - cannot reference `buf` data */
-int git_buf_join3(git_buf *buf, char separator, const char *str_a, const char *str_b, const char *str_c);
-
-/**
- * Join two strings as paths, inserting a slash between as needed.
- * @return 0 on success, -1 on failure
- */
-GIT_INLINE(int) git_buf_joinpath(git_buf *buf, const char *a, const char *b)
-{
- return git_buf_join(buf, '/', a, b);
-}
-
-GIT_INLINE(const char *) git_buf_cstr(const git_buf *buf)
-{
- return buf->ptr;
-}
-
-GIT_INLINE(size_t) git_buf_len(const git_buf *buf)
-{
- return buf->size;
-}
-
-void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
-
-#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
-
-GIT_INLINE(ssize_t) git_buf_rfind_next(const git_buf *buf, char ch)
-{
- ssize_t idx = (ssize_t)buf->size - 1;
- while (idx >= 0 && buf->ptr[idx] == ch) idx--;
- while (idx >= 0 && buf->ptr[idx] != ch) idx--;
- return idx;
-}
-
-GIT_INLINE(ssize_t) git_buf_rfind(const git_buf *buf, char ch)
-{
- ssize_t idx = (ssize_t)buf->size - 1;
- while (idx >= 0 && buf->ptr[idx] != ch) idx--;
- return idx;
-}
-
-GIT_INLINE(ssize_t) git_buf_find(const git_buf *buf, char ch)
-{
- void *found = memchr(buf->ptr, ch, buf->size);
- return found ? (ssize_t)((const char *)found - buf->ptr) : -1;
-}
-
-/* Remove whitespace from the end of the buffer */
-void git_buf_rtrim(git_buf *buf);
-
-int git_buf_cmp(const git_buf *a, const git_buf *b);
-
-/* Quote and unquote a buffer as specified in
- * http://marc.info/?l=git&m=112927316408690&w=2
- */
-int git_buf_quote(git_buf *buf);
-int git_buf_unquote(git_buf *buf);
-
-/* Write data as base64 encoded in buffer */
-int git_buf_encode_base64(git_buf *buf, const char *data, size_t len);
-/* Decode the given bas64 and write the result to the buffer */
-int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len);
-
-/* Write data as "base85" encoded in buffer */
-int git_buf_encode_base85(git_buf *buf, const char *data, size_t len);
-/* Decode the given "base85" and write the result to the buffer */
-int git_buf_decode_base85(git_buf *buf, const char *base64, size_t len, size_t output_len);
-
-/*
- * Insert, remove or replace a portion of the buffer.
- *
- * @param buf The buffer to work with
- *
- * @param where The location in the buffer where the transformation
- * should be applied.
- *
- * @param nb_to_remove The number of chars to be removed. 0 to not
- * remove any character in the buffer.
- *
- * @param data A pointer to the data which should be inserted.
- *
- * @param nb_to_insert The number of chars to be inserted. 0 to not
- * insert any character from the buffer.
- *
- * @return 0 or an error code.
- */
-int git_buf_splice(
- git_buf *buf,
- size_t where,
- size_t nb_to_remove,
- const char *data,
- size_t nb_to_insert);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "repository.h"
-#include "commit.h"
-#include "thread-utils.h"
-#include "util.h"
-#include "cache.h"
-#include "odb.h"
-#include "object.h"
-#include "git2/oid.h"
-
-GIT__USE_OIDMAP
-
-bool git_cache__enabled = true;
-ssize_t git_cache__max_storage = (256 * 1024 * 1024);
-git_atomic_ssize git_cache__current_storage = {0};
-
-static size_t git_cache__max_object_size[8] = {
- 0, /* GIT_OBJ__EXT1 */
- 4096, /* GIT_OBJ_COMMIT */
- 4096, /* GIT_OBJ_TREE */
- 0, /* GIT_OBJ_BLOB */
- 4096, /* GIT_OBJ_TAG */
- 0, /* GIT_OBJ__EXT2 */
- 0, /* GIT_OBJ_OFS_DELTA */
- 0 /* GIT_OBJ_REF_DELTA */
-};
-
-int git_cache_set_max_object_size(git_otype type, size_t size)
-{
- if (type < 0 || (size_t)type >= ARRAY_SIZE(git_cache__max_object_size)) {
- giterr_set(GITERR_INVALID, "type out of range");
- return -1;
- }
-
- git_cache__max_object_size[type] = size;
- return 0;
-}
-
-void git_cache_dump_stats(git_cache *cache)
-{
- git_cached_obj *object;
-
- if (kh_size(cache->map) == 0)
- return;
-
- printf("Cache %p: %d items cached, %"PRIdZ" bytes\n",
- cache, kh_size(cache->map), cache->used_memory);
-
- kh_foreach_value(cache->map, object, {
- char oid_str[9];
- printf(" %s%c %s (%"PRIuZ")\n",
- git_object_type2string(object->type),
- object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ',
- git_oid_tostr(oid_str, sizeof(oid_str), &object->oid),
- object->size
- );
- });
-}
-
-int git_cache_init(git_cache *cache)
-{
- memset(cache, 0, sizeof(*cache));
- cache->map = git_oidmap_alloc();
- GITERR_CHECK_ALLOC(cache->map);
- if (git_rwlock_init(&cache->lock)) {
- giterr_set(GITERR_OS, "Failed to initialize cache rwlock");
- return -1;
- }
- return 0;
-}
-
-/* called with lock */
-static void clear_cache(git_cache *cache)
-{
- git_cached_obj *evict = NULL;
-
- if (kh_size(cache->map) == 0)
- return;
-
- kh_foreach_value(cache->map, evict, {
- git_cached_obj_decref(evict);
- });
-
- kh_clear(oid, cache->map);
- git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory);
- cache->used_memory = 0;
-}
-
-void git_cache_clear(git_cache *cache)
-{
- if (git_rwlock_wrlock(&cache->lock) < 0)
- return;
-
- clear_cache(cache);
-
- git_rwlock_wrunlock(&cache->lock);
-}
-
-void git_cache_free(git_cache *cache)
-{
- git_cache_clear(cache);
- git_oidmap_free(cache->map);
- git_rwlock_free(&cache->lock);
- git__memzero(cache, sizeof(*cache));
-}
-
-/* Called with lock */
-static void cache_evict_entries(git_cache *cache)
-{
- uint32_t seed = rand();
- size_t evict_count = 8;
- ssize_t evicted_memory = 0;
-
- /* do not infinite loop if there's not enough entries to evict */
- if (evict_count > kh_size(cache->map)) {
- clear_cache(cache);
- return;
- }
-
- while (evict_count > 0) {
- khiter_t pos = seed++ % kh_end(cache->map);
-
- if (kh_exist(cache->map, pos)) {
- git_cached_obj *evict = kh_val(cache->map, pos);
-
- evict_count--;
- evicted_memory += evict->size;
- git_cached_obj_decref(evict);
-
- kh_del(oid, cache->map, pos);
- }
- }
-
- cache->used_memory -= evicted_memory;
- git_atomic_ssize_add(&git_cache__current_storage, -evicted_memory);
-}
-
-static bool cache_should_store(git_otype object_type, size_t object_size)
-{
- size_t max_size = git_cache__max_object_size[object_type];
- return git_cache__enabled && object_size < max_size;
-}
-
-static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
-{
- khiter_t pos;
- git_cached_obj *entry = NULL;
-
- if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0)
- return NULL;
-
- pos = kh_get(oid, cache->map, oid);
- if (pos != kh_end(cache->map)) {
- entry = kh_val(cache->map, pos);
-
- if (flags && entry->flags != flags) {
- entry = NULL;
- } else {
- git_cached_obj_incref(entry);
- }
- }
-
- git_rwlock_rdunlock(&cache->lock);
-
- return entry;
-}
-
-static void *cache_store(git_cache *cache, git_cached_obj *entry)
-{
- khiter_t pos;
-
- git_cached_obj_incref(entry);
-
- if (!git_cache__enabled && cache->used_memory > 0) {
- git_cache_clear(cache);
- return entry;
- }
-
- if (!cache_should_store(entry->type, entry->size))
- return entry;
-
- if (git_rwlock_wrlock(&cache->lock) < 0)
- return entry;
-
- /* soften the load on the cache */
- if (git_cache__current_storage.val > git_cache__max_storage)
- cache_evict_entries(cache);
-
- pos = kh_get(oid, cache->map, &entry->oid);
-
- /* not found */
- if (pos == kh_end(cache->map)) {
- int rval;
-
- pos = kh_put(oid, cache->map, &entry->oid, &rval);
- if (rval >= 0) {
- kh_key(cache->map, pos) = &entry->oid;
- kh_val(cache->map, pos) = entry;
- git_cached_obj_incref(entry);
- cache->used_memory += entry->size;
- git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size);
- }
- }
- /* found */
- else {
- git_cached_obj *stored_entry = kh_val(cache->map, pos);
-
- if (stored_entry->flags == entry->flags) {
- git_cached_obj_decref(entry);
- git_cached_obj_incref(stored_entry);
- entry = stored_entry;
- } else if (stored_entry->flags == GIT_CACHE_STORE_RAW &&
- entry->flags == GIT_CACHE_STORE_PARSED) {
- git_cached_obj_decref(stored_entry);
- git_cached_obj_incref(entry);
-
- kh_key(cache->map, pos) = &entry->oid;
- kh_val(cache->map, pos) = entry;
- } else {
- /* NO OP */
- }
- }
-
- git_rwlock_wrunlock(&cache->lock);
- return entry;
-}
-
-void *git_cache_store_raw(git_cache *cache, git_odb_object *entry)
-{
- entry->cached.flags = GIT_CACHE_STORE_RAW;
- return cache_store(cache, (git_cached_obj *)entry);
-}
-
-void *git_cache_store_parsed(git_cache *cache, git_object *entry)
-{
- entry->cached.flags = GIT_CACHE_STORE_PARSED;
- return cache_store(cache, (git_cached_obj *)entry);
-}
-
-git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid)
-{
- return cache_get(cache, oid, GIT_CACHE_STORE_RAW);
-}
-
-git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid)
-{
- return cache_get(cache, oid, GIT_CACHE_STORE_PARSED);
-}
-
-void *git_cache_get_any(git_cache *cache, const git_oid *oid)
-{
- return cache_get(cache, oid, GIT_CACHE_STORE_ANY);
-}
-
-void git_cached_obj_decref(void *_obj)
-{
- git_cached_obj *obj = _obj;
-
- if (git_atomic_dec(&obj->refcount) == 0) {
- switch (obj->flags) {
- case GIT_CACHE_STORE_RAW:
- git_odb_object__free(_obj);
- break;
-
- case GIT_CACHE_STORE_PARSED:
- git_object__free(_obj);
- break;
-
- default:
- git__free(_obj);
- break;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_cache_h__
-#define INCLUDE_cache_h__
-
-#include "git2/common.h"
-#include "git2/oid.h"
-#include "git2/odb.h"
-
-#include "thread-utils.h"
-#include "oidmap.h"
-
-enum {
- GIT_CACHE_STORE_ANY = 0,
- GIT_CACHE_STORE_RAW = 1,
- GIT_CACHE_STORE_PARSED = 2
-};
-
-typedef struct {
- git_oid oid;
- int16_t type; /* git_otype value */
- uint16_t flags; /* GIT_CACHE_STORE value */
- size_t size;
- git_atomic refcount;
-} git_cached_obj;
-
-typedef struct {
- git_oidmap *map;
- git_rwlock lock;
- ssize_t used_memory;
-} git_cache;
-
-extern bool git_cache__enabled;
-extern ssize_t git_cache__max_storage;
-extern git_atomic_ssize git_cache__current_storage;
-
-int git_cache_set_max_object_size(git_otype type, size_t size);
-
-int git_cache_init(git_cache *cache);
-void git_cache_free(git_cache *cache);
-void git_cache_clear(git_cache *cache);
-
-void *git_cache_store_raw(git_cache *cache, git_odb_object *entry);
-void *git_cache_store_parsed(git_cache *cache, git_object *entry);
-
-git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid);
-git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid);
-void *git_cache_get_any(git_cache *cache, const git_oid *oid);
-
-GIT_INLINE(size_t) git_cache_size(git_cache *cache)
-{
- return (size_t)kh_size(cache->map);
-}
-
-GIT_INLINE(void) git_cached_obj_incref(void *_obj)
-{
- git_cached_obj *obj = _obj;
- git_atomic_inc(&obj->refcount);
-}
-
-void git_cached_obj_decref(void *_obj);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_compat_h__
-#define INCLUDE_compat_h__
-
-#include <stdarg.h>
-
-/*
- * See if our compiler is known to support flexible array members.
- */
-#ifndef GIT_FLEX_ARRAY
-# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-# define GIT_FLEX_ARRAY /* empty */
-# elif defined(__GNUC__)
-# if (__GNUC__ >= 3)
-# define GIT_FLEX_ARRAY /* empty */
-# else
-# define GIT_FLEX_ARRAY 0 /* older GNU extension */
-# endif
-# endif
-
-/* Default to safer but a bit wasteful traditional style */
-# ifndef GIT_FLEX_ARRAY
-# define GIT_FLEX_ARRAY 1
-# endif
-#endif
-
-#ifdef __GNUC__
-# define GIT_TYPEOF(x) (__typeof__(x))
-#else
-# define GIT_TYPEOF(x)
-#endif
-
-#if defined(__GNUC__)
-# define GIT_ALIGN(x,size) x __attribute__ ((aligned(size)))
-#elif defined(_MSC_VER)
-# define GIT_ALIGN(x,size) __declspec(align(size)) x
-#else
-# define GIT_ALIGN(x,size) x
-#endif
-
-#define GIT_UNUSED(x) ((void)(x))
-
-/* Define the printf format specifer to use for size_t output */
-#if defined(_MSC_VER) || defined(__MINGW32__)
-# define PRIuZ "Iu"
-# define PRIxZ "Ix"
-# define PRIdZ "Id"
-#else
-# define PRIuZ "zu"
-# define PRIxZ "zx"
-# define PRIdZ "zd"
-#endif
-
-/* Micosoft Visual C/C++ */
-#if defined(_MSC_VER)
-/* disable "deprecated function" warnings */
-# pragma warning ( disable : 4996 )
-/* disable "conditional expression is constant" level 4 warnings */
-# pragma warning ( disable : 4127 )
-#endif
-
-#if defined (_MSC_VER)
- typedef unsigned char bool;
-# ifndef true
-# define true 1
-# endif
-# ifndef false
-# define false 0
-# endif
-#else
-# include <stdbool.h>
-#endif
-
-#ifndef va_copy
-# ifdef __va_copy
-# define va_copy(dst, src) __va_copy(dst, src)
-# else
-# define va_copy(dst, src) ((dst) = (src))
-# endif
-#endif
-
-#endif /* INCLUDE_compat_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include <assert.h>
-
-#include "checkout.h"
-
-#include "git2/repository.h"
-#include "git2/refs.h"
-#include "git2/tree.h"
-#include "git2/blob.h"
-#include "git2/config.h"
-#include "git2/diff.h"
-#include "git2/submodule.h"
-#include "git2/sys/index.h"
-#include "git2/sys/filter.h"
-#include "git2/merge.h"
-
-#include "refs.h"
-#include "repository.h"
-#include "index.h"
-#include "filter.h"
-#include "blob.h"
-#include "diff.h"
-#include "diff_generate.h"
-#include "pathspec.h"
-#include "buf_text.h"
-#include "diff_xdiff.h"
-#include "path.h"
-#include "attr.h"
-#include "pool.h"
-#include "strmap.h"
-
-GIT__USE_STRMAP
-
-/* See docs/checkout-internals.md for more information */
-
-enum {
- CHECKOUT_ACTION__NONE = 0,
- CHECKOUT_ACTION__REMOVE = 1,
- CHECKOUT_ACTION__UPDATE_BLOB = 2,
- CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
- CHECKOUT_ACTION__CONFLICT = 8,
- CHECKOUT_ACTION__REMOVE_CONFLICT = 16,
- CHECKOUT_ACTION__UPDATE_CONFLICT = 32,
- CHECKOUT_ACTION__MAX = 32,
- CHECKOUT_ACTION__DEFER_REMOVE = 64,
- CHECKOUT_ACTION__REMOVE_AND_UPDATE =
- (CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE),
-};
-
-typedef struct {
- git_repository *repo;
- git_iterator *target;
- git_diff *diff;
- git_checkout_options opts;
- bool opts_free_baseline;
- char *pfx;
- git_index *index;
- git_pool pool;
- git_vector removes;
- git_vector remove_conflicts;
- git_vector update_conflicts;
- git_vector *update_reuc;
- git_vector *update_names;
- git_buf target_path;
- size_t target_len;
- git_buf tmp;
- unsigned int strategy;
- int can_symlink;
- bool reload_submodules;
- size_t total_steps;
- size_t completed_steps;
- git_checkout_perfdata perfdata;
- git_strmap *mkdir_map;
- git_attr_session attr_session;
-} checkout_data;
-
-typedef struct {
- const git_index_entry *ancestor;
- const git_index_entry *ours;
- const git_index_entry *theirs;
-
- int name_collision:1,
- directoryfile:1,
- one_to_two:1,
- binary:1,
- submodule:1;
-} checkout_conflictdata;
-
-static int checkout_notify(
- checkout_data *data,
- git_checkout_notify_t why,
- const git_diff_delta *delta,
- const git_index_entry *wditem)
-{
- git_diff_file wdfile;
- const git_diff_file *baseline = NULL, *target = NULL, *workdir = NULL;
- const char *path = NULL;
-
- if (!data->opts.notify_cb ||
- (why & data->opts.notify_flags) == 0)
- return 0;
-
- if (wditem) {
- memset(&wdfile, 0, sizeof(wdfile));
-
- git_oid_cpy(&wdfile.id, &wditem->id);
- wdfile.path = wditem->path;
- wdfile.size = wditem->file_size;
- wdfile.flags = GIT_DIFF_FLAG_VALID_ID;
- wdfile.mode = wditem->mode;
-
- workdir = &wdfile;
-
- path = wditem->path;
- }
-
- if (delta) {
- switch (delta->status) {
- case GIT_DELTA_UNMODIFIED:
- case GIT_DELTA_MODIFIED:
- case GIT_DELTA_TYPECHANGE:
- default:
- baseline = &delta->old_file;
- target = &delta->new_file;
- break;
- case GIT_DELTA_ADDED:
- case GIT_DELTA_IGNORED:
- case GIT_DELTA_UNTRACKED:
- case GIT_DELTA_UNREADABLE:
- target = &delta->new_file;
- break;
- case GIT_DELTA_DELETED:
- baseline = &delta->old_file;
- break;
- }
-
- path = delta->old_file.path;
- }
-
- {
- int error = data->opts.notify_cb(
- why, path, baseline, target, workdir, data->opts.notify_payload);
-
- return giterr_set_after_callback_function(
- error, "git_checkout notification");
- }
-}
-
-GIT_INLINE(bool) is_workdir_base_or_new(
- const git_oid *workdir_id,
- const git_diff_file *baseitem,
- const git_diff_file *newitem)
-{
- return (git_oid__cmp(&baseitem->id, workdir_id) == 0 ||
- git_oid__cmp(&newitem->id, workdir_id) == 0);
-}
-
-static bool checkout_is_workdir_modified(
- checkout_data *data,
- const git_diff_file *baseitem,
- const git_diff_file *newitem,
- const git_index_entry *wditem)
-{
- git_oid oid;
- const git_index_entry *ie;
-
- /* handle "modified" submodule */
- if (wditem->mode == GIT_FILEMODE_COMMIT) {
- git_submodule *sm;
- unsigned int sm_status = 0;
- const git_oid *sm_oid = NULL;
- bool rval = false;
-
- if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0) {
- giterr_clear();
- return true;
- }
-
- if (git_submodule_status(&sm_status, data->repo, wditem->path, GIT_SUBMODULE_IGNORE_UNSPECIFIED) < 0 ||
- GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
- rval = true;
- else if ((sm_oid = git_submodule_wd_id(sm)) == NULL)
- rval = false;
- else
- rval = (git_oid__cmp(&baseitem->id, sm_oid) != 0);
-
- git_submodule_free(sm);
- return rval;
- }
-
- /* Look at the cache to decide if the workdir is modified. If not,
- * we can simply compare the oid in the cache to the baseitem instead
- * of hashing the file. If so, we allow the checkout to proceed if the
- * oid is identical (ie, the staged item is what we're trying to check
- * out.)
- */
- if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) {
- if (git_index_time_eq(&wditem->mtime, &ie->mtime) &&
- wditem->file_size == ie->file_size)
- return !is_workdir_base_or_new(&ie->id, baseitem, newitem);
- }
-
- /* depending on where base is coming from, we may or may not know
- * the actual size of the data, so we can't rely on this shortcut.
- */
- if (baseitem->size && wditem->file_size != baseitem->size)
- return true;
-
- /* if the workdir item is a directory, it cannot be a modified file */
- if (S_ISDIR(wditem->mode))
- return false;
-
- if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0)
- return false;
-
- /* Allow the checkout if the workdir is not modified *or* if the checkout
- * target's contents are already in the working directory.
- */
- return !is_workdir_base_or_new(&oid, baseitem, newitem);
-}
-
-#define CHECKOUT_ACTION_IF(FLAG,YES,NO) \
- ((data->strategy & GIT_CHECKOUT_##FLAG) ? CHECKOUT_ACTION__##YES : CHECKOUT_ACTION__##NO)
-
-static int checkout_action_common(
- int *action,
- checkout_data *data,
- const git_diff_delta *delta,
- const git_index_entry *wd)
-{
- git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
-
- if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
- *action = (*action & ~CHECKOUT_ACTION__REMOVE);
-
- if ((*action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) {
- if (S_ISGITLINK(delta->new_file.mode))
- *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB) |
- CHECKOUT_ACTION__UPDATE_SUBMODULE;
-
- /* to "update" a symlink, we must remove the old one first */
- if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL)
- *action |= CHECKOUT_ACTION__REMOVE;
-
- /* if the file is on disk and doesn't match our mode, force update */
- if (wd &&
- GIT_PERMS_IS_EXEC(wd->mode) !=
- GIT_PERMS_IS_EXEC(delta->new_file.mode))
- *action |= CHECKOUT_ACTION__REMOVE;
-
- notify = GIT_CHECKOUT_NOTIFY_UPDATED;
- }
-
- if ((*action & CHECKOUT_ACTION__CONFLICT) != 0)
- notify = GIT_CHECKOUT_NOTIFY_CONFLICT;
-
- return checkout_notify(data, notify, delta, wd);
-}
-
-static int checkout_action_no_wd(
- int *action,
- checkout_data *data,
- const git_diff_delta *delta)
-{
- int error = 0;
-
- *action = CHECKOUT_ACTION__NONE;
-
- switch (delta->status) {
- case GIT_DELTA_UNMODIFIED: /* case 12 */
- error = checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL);
- if (error)
- return error;
- *action = CHECKOUT_ACTION_IF(RECREATE_MISSING, UPDATE_BLOB, NONE);
- break;
- case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */
- *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
- break;
- case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
- *action = CHECKOUT_ACTION_IF(RECREATE_MISSING, UPDATE_BLOB, CONFLICT);
- break;
- case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/
- if (delta->new_file.mode == GIT_FILEMODE_TREE)
- *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
- break;
- case GIT_DELTA_DELETED: /* case 8 or 25 */
- *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
- break;
- default: /* impossible */
- break;
- }
-
- return checkout_action_common(action, data, delta, NULL);
-}
-
-static int checkout_target_fullpath(
- git_buf **out, checkout_data *data, const char *path)
-{
- git_buf_truncate(&data->target_path, data->target_len);
-
- if (path && git_buf_puts(&data->target_path, path) < 0)
- return -1;
-
- *out = &data->target_path;
-
- return 0;
-}
-
-static bool wd_item_is_removable(
- checkout_data *data, const git_index_entry *wd)
-{
- git_buf *full;
-
- if (wd->mode != GIT_FILEMODE_TREE)
- return true;
-
- if (checkout_target_fullpath(&full, data, wd->path) < 0)
- return false;
-
- return !full || !git_path_contains(full, DOT_GIT);
-}
-
-static int checkout_queue_remove(checkout_data *data, const char *path)
-{
- char *copy = git_pool_strdup(&data->pool, path);
- GITERR_CHECK_ALLOC(copy);
- return git_vector_insert(&data->removes, copy);
-}
-
-/* note that this advances the iterator over the wd item */
-static int checkout_action_wd_only(
- checkout_data *data,
- git_iterator *workdir,
- const git_index_entry **wditem,
- git_vector *pathspec)
-{
- int error = 0;
- bool remove = false;
- git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
- const git_index_entry *wd = *wditem;
-
- if (!git_pathspec__match(
- pathspec, wd->path,
- (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
- git_iterator_ignore_case(workdir), NULL, NULL))
- return git_iterator_advance(wditem, workdir);
-
- /* check if item is tracked in the index but not in the checkout diff */
- if (data->index != NULL) {
- size_t pos;
-
- error = git_index__find_pos(
- &pos, data->index, wd->path, 0, GIT_INDEX_STAGE_ANY);
-
- if (wd->mode != GIT_FILEMODE_TREE) {
- if (!error) { /* found by git_index__find_pos call */
- notify = GIT_CHECKOUT_NOTIFY_DIRTY;
- remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
- } else if (error != GIT_ENOTFOUND)
- return error;
- else
- error = 0; /* git_index__find_pos does not set error msg */
- } else {
- /* for tree entries, we have to see if there are any index
- * entries that are contained inside that tree
- */
- const git_index_entry *e = git_index_get_byindex(data->index, pos);
-
- if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) {
- notify = GIT_CHECKOUT_NOTIFY_DIRTY;
- remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
- }
- }
- }
-
- if (notify != GIT_CHECKOUT_NOTIFY_NONE) {
- /* if we found something in the index, notify and advance */
- if ((error = checkout_notify(data, notify, NULL, wd)) != 0)
- return error;
-
- if (remove && wd_item_is_removable(data, wd))
- error = checkout_queue_remove(data, wd->path);
-
- if (!error)
- error = git_iterator_advance(wditem, workdir);
- } else {
- /* untracked or ignored - can't know which until we advance through */
- bool over = false, removable = wd_item_is_removable(data, wd);
- git_iterator_status_t untracked_state;
-
- /* copy the entry for issuing notification callback later */
- git_index_entry saved_wd = *wd;
- git_buf_sets(&data->tmp, wd->path);
- saved_wd.path = data->tmp.ptr;
-
- error = git_iterator_advance_over(
- wditem, &untracked_state, workdir);
- if (error == GIT_ITEROVER)
- over = true;
- else if (error < 0)
- return error;
-
- if (untracked_state == GIT_ITERATOR_STATUS_IGNORED) {
- notify = GIT_CHECKOUT_NOTIFY_IGNORED;
- remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0);
- } else {
- notify = GIT_CHECKOUT_NOTIFY_UNTRACKED;
- remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0);
- }
-
- if ((error = checkout_notify(data, notify, NULL, &saved_wd)) != 0)
- return error;
-
- if (remove && removable)
- error = checkout_queue_remove(data, saved_wd.path);
-
- if (!error && over) /* restore ITEROVER if needed */
- error = GIT_ITEROVER;
- }
-
- return error;
-}
-
-static bool submodule_is_config_only(
- checkout_data *data,
- const char *path)
-{
- git_submodule *sm = NULL;
- unsigned int sm_loc = 0;
- bool rval = false;
-
- if (git_submodule_lookup(&sm, data->repo, path) < 0)
- return true;
-
- if (git_submodule_location(&sm_loc, sm) < 0 ||
- sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG)
- rval = true;
-
- git_submodule_free(sm);
-
- return rval;
-}
-
-static bool checkout_is_empty_dir(checkout_data *data, const char *path)
-{
- git_buf *fullpath;
-
- if (checkout_target_fullpath(&fullpath, data, path) < 0)
- return false;
-
- return git_path_is_empty_dir(fullpath->ptr);
-}
-
-static int checkout_action_with_wd(
- int *action,
- checkout_data *data,
- const git_diff_delta *delta,
- git_iterator *workdir,
- const git_index_entry *wd)
-{
- *action = CHECKOUT_ACTION__NONE;
-
- switch (delta->status) {
- case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */
- if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) {
- GITERR_CHECK_ERROR(
- checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) );
- *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE);
- }
- break;
- case GIT_DELTA_ADDED: /* case 3, 4 or 6 */
- if (git_iterator_current_is_ignored(workdir))
- *action = CHECKOUT_ACTION_IF(DONT_OVERWRITE_IGNORED, CONFLICT, UPDATE_BLOB);
- else
- *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
- break;
- case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */
- if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
- *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
- else
- *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
- break;
- case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */
- if (wd->mode != GIT_FILEMODE_COMMIT &&
- checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
- *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
- else
- *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
- break;
- case GIT_DELTA_TYPECHANGE: /* case 22, 23, 29, 30 */
- if (delta->old_file.mode == GIT_FILEMODE_TREE) {
- if (wd->mode == GIT_FILEMODE_TREE)
- /* either deleting items in old tree will delete the wd dir,
- * or we'll get a conflict when we attempt blob update...
- */
- *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
- else if (wd->mode == GIT_FILEMODE_COMMIT) {
- /* workdir is possibly a "phantom" submodule - treat as a
- * tree if the only submodule info came from the config
- */
- if (submodule_is_config_only(data, wd->path))
- *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
- else
- *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
- } else
- *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
- }
- else if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
- *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
- else
- *action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE);
-
- /* don't update if the typechange is to a tree */
- if (delta->new_file.mode == GIT_FILEMODE_TREE)
- *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB);
- break;
- default: /* impossible */
- break;
- }
-
- return checkout_action_common(action, data, delta, wd);
-}
-
-static int checkout_action_with_wd_blocker(
- int *action,
- checkout_data *data,
- const git_diff_delta *delta,
- const git_index_entry *wd)
-{
- *action = CHECKOUT_ACTION__NONE;
-
- switch (delta->status) {
- case GIT_DELTA_UNMODIFIED:
- /* should show delta as dirty / deleted */
- GITERR_CHECK_ERROR(
- checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) );
- *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE);
- break;
- case GIT_DELTA_ADDED:
- case GIT_DELTA_MODIFIED:
- *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
- break;
- case GIT_DELTA_DELETED:
- *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
- break;
- case GIT_DELTA_TYPECHANGE:
- /* not 100% certain about this... */
- *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
- break;
- default: /* impossible */
- break;
- }
-
- return checkout_action_common(action, data, delta, wd);
-}
-
-static int checkout_action_with_wd_dir(
- int *action,
- checkout_data *data,
- const git_diff_delta *delta,
- git_iterator *workdir,
- const git_index_entry *wd)
-{
- *action = CHECKOUT_ACTION__NONE;
-
- switch (delta->status) {
- case GIT_DELTA_UNMODIFIED: /* case 19 or 24 (or 34 but not really) */
- GITERR_CHECK_ERROR(
- checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL));
- GITERR_CHECK_ERROR(
- checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd));
- *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE);
- break;
- case GIT_DELTA_ADDED:/* case 4 (and 7 for dir) */
- case GIT_DELTA_MODIFIED: /* case 20 (or 37 but not really) */
- if (delta->old_file.mode == GIT_FILEMODE_COMMIT)
- /* expected submodule (and maybe found one) */;
- else if (delta->new_file.mode != GIT_FILEMODE_TREE)
- *action = git_iterator_current_is_ignored(workdir) ?
- CHECKOUT_ACTION_IF(DONT_OVERWRITE_IGNORED, CONFLICT, REMOVE_AND_UPDATE) :
- CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
- break;
- case GIT_DELTA_DELETED: /* case 11 (and 27 for dir) */
- if (delta->old_file.mode != GIT_FILEMODE_TREE)
- GITERR_CHECK_ERROR(
- checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd));
- break;
- case GIT_DELTA_TYPECHANGE: /* case 24 or 31 */
- if (delta->old_file.mode == GIT_FILEMODE_TREE) {
- /* For typechange from dir, remove dir and add blob, but it is
- * not safe to remove dir if it contains modified files.
- * However, safely removing child files will remove the parent
- * directory if is it left empty, so we can defer removing the
- * dir and it will succeed if no children are left.
- */
- *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
- }
- else if (delta->new_file.mode != GIT_FILEMODE_TREE)
- /* For typechange to dir, dir is already created so no action */
- *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
- break;
- default: /* impossible */
- break;
- }
-
- return checkout_action_common(action, data, delta, wd);
-}
-
-static int checkout_action_with_wd_dir_empty(
- int *action,
- checkout_data *data,
- const git_diff_delta *delta)
-{
- int error = checkout_action_no_wd(action, data, delta);
-
- /* We can always safely remove an empty directory. */
- if (error == 0 && *action != CHECKOUT_ACTION__NONE)
- *action |= CHECKOUT_ACTION__REMOVE;
-
- return error;
-}
-
-static int checkout_action(
- int *action,
- checkout_data *data,
- git_diff_delta *delta,
- git_iterator *workdir,
- const git_index_entry **wditem,
- git_vector *pathspec)
-{
- int cmp = -1, error;
- int (*strcomp)(const char *, const char *) = data->diff->strcomp;
- int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp;
- int (*advance)(const git_index_entry **, git_iterator *) = NULL;
-
- /* move workdir iterator to follow along with deltas */
-
- while (1) {
- const git_index_entry *wd = *wditem;
-
- if (!wd)
- return checkout_action_no_wd(action, data, delta);
-
- cmp = strcomp(wd->path, delta->old_file.path);
-
- /* 1. wd before delta ("a/a" before "a/b")
- * 2. wd prefixes delta & should expand ("a/" before "a/b")
- * 3. wd prefixes delta & cannot expand ("a/b" before "a/b/c")
- * 4. wd equals delta ("a/b" and "a/b")
- * 5. wd after delta & delta prefixes wd ("a/b/c" after "a/b/" or "a/b")
- * 6. wd after delta ("a/c" after "a/b")
- */
-
- if (cmp < 0) {
- cmp = pfxcomp(delta->old_file.path, wd->path);
-
- if (cmp == 0) {
- if (wd->mode == GIT_FILEMODE_TREE) {
- /* case 2 - entry prefixed by workdir tree */
- error = git_iterator_advance_into(wditem, workdir);
- if (error < 0 && error != GIT_ITEROVER)
- goto done;
- continue;
- }
-
- /* case 3 maybe - wd contains non-dir where dir expected */
- if (delta->old_file.path[strlen(wd->path)] == '/') {
- error = checkout_action_with_wd_blocker(
- action, data, delta, wd);
- advance = git_iterator_advance;
- goto done;
- }
- }
-
- /* case 1 - handle wd item (if it matches pathspec) */
- error = checkout_action_wd_only(data, workdir, wditem, pathspec);
- if (error && error != GIT_ITEROVER)
- goto done;
- continue;
- }
-
- if (cmp == 0) {
- /* case 4 */
- error = checkout_action_with_wd(action, data, delta, workdir, wd);
- advance = git_iterator_advance;
- goto done;
- }
-
- cmp = pfxcomp(wd->path, delta->old_file.path);
-
- if (cmp == 0) { /* case 5 */
- if (wd->path[strlen(delta->old_file.path)] != '/')
- return checkout_action_no_wd(action, data, delta);
-
- if (delta->status == GIT_DELTA_TYPECHANGE) {
- if (delta->old_file.mode == GIT_FILEMODE_TREE) {
- error = checkout_action_with_wd(action, data, delta, workdir, wd);
- advance = git_iterator_advance_into;
- goto done;
- }
-
- if (delta->new_file.mode == GIT_FILEMODE_TREE ||
- delta->new_file.mode == GIT_FILEMODE_COMMIT ||
- delta->old_file.mode == GIT_FILEMODE_COMMIT)
- {
- error = checkout_action_with_wd(action, data, delta, workdir, wd);
- advance = git_iterator_advance;
- goto done;
- }
- }
-
- return checkout_is_empty_dir(data, wd->path) ?
- checkout_action_with_wd_dir_empty(action, data, delta) :
- checkout_action_with_wd_dir(action, data, delta, workdir, wd);
- }
-
- /* case 6 - wd is after delta */
- return checkout_action_no_wd(action, data, delta);
- }
-
-done:
- if (!error && advance != NULL &&
- (error = advance(wditem, workdir)) < 0) {
- *wditem = NULL;
- if (error == GIT_ITEROVER)
- error = 0;
- }
-
- return error;
-}
-
-static int checkout_remaining_wd_items(
- checkout_data *data,
- git_iterator *workdir,
- const git_index_entry *wd,
- git_vector *spec)
-{
- int error = 0;
-
- while (wd && !error)
- error = checkout_action_wd_only(data, workdir, &wd, spec);
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- return error;
-}
-
-GIT_INLINE(int) checkout_idxentry_cmp(
- const git_index_entry *a,
- const git_index_entry *b)
-{
- if (!a && !b)
- return 0;
- else if (!a && b)
- return -1;
- else if(a && !b)
- return 1;
- else
- return strcmp(a->path, b->path);
-}
-
-static int checkout_conflictdata_cmp(const void *a, const void *b)
-{
- const checkout_conflictdata *ca = a;
- const checkout_conflictdata *cb = b;
- int diff;
-
- if ((diff = checkout_idxentry_cmp(ca->ancestor, cb->ancestor)) == 0 &&
- (diff = checkout_idxentry_cmp(ca->ours, cb->theirs)) == 0)
- diff = checkout_idxentry_cmp(ca->theirs, cb->theirs);
-
- return diff;
-}
-
-int checkout_conflictdata_empty(
- const git_vector *conflicts, size_t idx, void *payload)
-{
- checkout_conflictdata *conflict;
-
- GIT_UNUSED(payload);
-
- if ((conflict = git_vector_get(conflicts, idx)) == NULL)
- return -1;
-
- if (conflict->ancestor || conflict->ours || conflict->theirs)
- return 0;
-
- git__free(conflict);
- return 1;
-}
-
-GIT_INLINE(bool) conflict_pathspec_match(
- checkout_data *data,
- git_iterator *workdir,
- git_vector *pathspec,
- const git_index_entry *ancestor,
- const git_index_entry *ours,
- const git_index_entry *theirs)
-{
- /* if the pathspec matches ours *or* theirs, proceed */
- if (ours && git_pathspec__match(pathspec, ours->path,
- (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
- git_iterator_ignore_case(workdir), NULL, NULL))
- return true;
-
- if (theirs && git_pathspec__match(pathspec, theirs->path,
- (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
- git_iterator_ignore_case(workdir), NULL, NULL))
- return true;
-
- if (ancestor && git_pathspec__match(pathspec, ancestor->path,
- (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
- git_iterator_ignore_case(workdir), NULL, NULL))
- return true;
-
- return false;
-}
-
-GIT_INLINE(int) checkout_conflict_detect_submodule(checkout_conflictdata *conflict)
-{
- conflict->submodule = ((conflict->ancestor && S_ISGITLINK(conflict->ancestor->mode)) ||
- (conflict->ours && S_ISGITLINK(conflict->ours->mode)) ||
- (conflict->theirs && S_ISGITLINK(conflict->theirs->mode)));
- return 0;
-}
-
-GIT_INLINE(int) checkout_conflict_detect_binary(git_repository *repo, checkout_conflictdata *conflict)
-{
- git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL;
- int error = 0;
-
- if (conflict->submodule)
- return 0;
-
- if (conflict->ancestor) {
- if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor->id)) < 0)
- goto done;
-
- conflict->binary = git_blob_is_binary(ancestor_blob);
- }
-
- if (!conflict->binary && conflict->ours) {
- if ((error = git_blob_lookup(&our_blob, repo, &conflict->ours->id)) < 0)
- goto done;
-
- conflict->binary = git_blob_is_binary(our_blob);
- }
-
- if (!conflict->binary && conflict->theirs) {
- if ((error = git_blob_lookup(&their_blob, repo, &conflict->theirs->id)) < 0)
- goto done;
-
- conflict->binary = git_blob_is_binary(their_blob);
- }
-
-done:
- git_blob_free(ancestor_blob);
- git_blob_free(our_blob);
- git_blob_free(their_blob);
-
- return error;
-}
-
-static int checkout_conflict_append_update(
- const git_index_entry *ancestor,
- const git_index_entry *ours,
- const git_index_entry *theirs,
- void *payload)
-{
- checkout_data *data = payload;
- checkout_conflictdata *conflict;
- int error;
-
- conflict = git__calloc(1, sizeof(checkout_conflictdata));
- GITERR_CHECK_ALLOC(conflict);
-
- conflict->ancestor = ancestor;
- conflict->ours = ours;
- conflict->theirs = theirs;
-
- if ((error = checkout_conflict_detect_submodule(conflict)) < 0 ||
- (error = checkout_conflict_detect_binary(data->repo, conflict)) < 0)
- {
- git__free(conflict);
- return error;
- }
-
- if (git_vector_insert(&data->update_conflicts, conflict))
- return -1;
-
- return 0;
-}
-
-static int checkout_conflicts_foreach(
- checkout_data *data,
- git_index *index,
- git_iterator *workdir,
- git_vector *pathspec,
- int (*cb)(const git_index_entry *, const git_index_entry *, const git_index_entry *, void *),
- void *payload)
-{
- git_index_conflict_iterator *iterator = NULL;
- const git_index_entry *ancestor, *ours, *theirs;
- int error = 0;
-
- if ((error = git_index_conflict_iterator_new(&iterator, index)) < 0)
- goto done;
-
- /* Collect the conflicts */
- while ((error = git_index_conflict_next(&ancestor, &ours, &theirs, iterator)) == 0) {
- if (!conflict_pathspec_match(data, workdir, pathspec, ancestor, ours, theirs))
- continue;
-
- if ((error = cb(ancestor, ours, theirs, payload)) < 0)
- goto done;
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
-done:
- git_index_conflict_iterator_free(iterator);
-
- return error;
-}
-
-static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
-{
- git_index *index;
-
- /* Only write conficts from sources that have them: indexes. */
- if ((index = git_iterator_index(data->target)) == NULL)
- return 0;
-
- data->update_conflicts._cmp = checkout_conflictdata_cmp;
-
- if (checkout_conflicts_foreach(data, index, workdir, pathspec, checkout_conflict_append_update, data) < 0)
- return -1;
-
- /* Collect the REUC and NAME entries */
- data->update_reuc = &index->reuc;
- data->update_names = &index->names;
-
- return 0;
-}
-
-GIT_INLINE(int) checkout_conflicts_cmp_entry(
- const char *path,
- const git_index_entry *entry)
-{
- return strcmp((const char *)path, entry->path);
-}
-
-static int checkout_conflicts_cmp_ancestor(const void *p, const void *c)
-{
- const char *path = p;
- const checkout_conflictdata *conflict = c;
-
- if (!conflict->ancestor)
- return 1;
-
- return checkout_conflicts_cmp_entry(path, conflict->ancestor);
-}
-
-static checkout_conflictdata *checkout_conflicts_search_ancestor(
- checkout_data *data,
- const char *path)
-{
- size_t pos;
-
- if (git_vector_bsearch2(&pos, &data->update_conflicts, checkout_conflicts_cmp_ancestor, path) < 0)
- return NULL;
-
- return git_vector_get(&data->update_conflicts, pos);
-}
-
-static checkout_conflictdata *checkout_conflicts_search_branch(
- checkout_data *data,
- const char *path)
-{
- checkout_conflictdata *conflict;
- size_t i;
-
- git_vector_foreach(&data->update_conflicts, i, conflict) {
- int cmp = -1;
-
- if (conflict->ancestor)
- break;
-
- if (conflict->ours)
- cmp = checkout_conflicts_cmp_entry(path, conflict->ours);
- else if (conflict->theirs)
- cmp = checkout_conflicts_cmp_entry(path, conflict->theirs);
-
- if (cmp == 0)
- return conflict;
- }
-
- return NULL;
-}
-
-static int checkout_conflicts_load_byname_entry(
- checkout_conflictdata **ancestor_out,
- checkout_conflictdata **ours_out,
- checkout_conflictdata **theirs_out,
- checkout_data *data,
- const git_index_name_entry *name_entry)
-{
- checkout_conflictdata *ancestor, *ours = NULL, *theirs = NULL;
- int error = 0;
-
- *ancestor_out = NULL;
- *ours_out = NULL;
- *theirs_out = NULL;
-
- if (!name_entry->ancestor) {
- giterr_set(GITERR_INDEX, "A NAME entry exists without an ancestor");
- error = -1;
- goto done;
- }
-
- if (!name_entry->ours && !name_entry->theirs) {
- giterr_set(GITERR_INDEX, "A NAME entry exists without an ours or theirs");
- error = -1;
- goto done;
- }
-
- if ((ancestor = checkout_conflicts_search_ancestor(data,
- name_entry->ancestor)) == NULL) {
- giterr_set(GITERR_INDEX,
- "A NAME entry referenced ancestor entry '%s' which does not exist in the main index",
- name_entry->ancestor);
- error = -1;
- goto done;
- }
-
- if (name_entry->ours) {
- if (strcmp(name_entry->ancestor, name_entry->ours) == 0)
- ours = ancestor;
- else if ((ours = checkout_conflicts_search_branch(data, name_entry->ours)) == NULL ||
- ours->ours == NULL) {
- giterr_set(GITERR_INDEX,
- "A NAME entry referenced our entry '%s' which does not exist in the main index",
- name_entry->ours);
- error = -1;
- goto done;
- }
- }
-
- if (name_entry->theirs) {
- if (strcmp(name_entry->ancestor, name_entry->theirs) == 0)
- theirs = ancestor;
- else if (name_entry->ours && strcmp(name_entry->ours, name_entry->theirs) == 0)
- theirs = ours;
- else if ((theirs = checkout_conflicts_search_branch(data, name_entry->theirs)) == NULL ||
- theirs->theirs == NULL) {
- giterr_set(GITERR_INDEX,
- "A NAME entry referenced their entry '%s' which does not exist in the main index",
- name_entry->theirs);
- error = -1;
- goto done;
- }
- }
-
- *ancestor_out = ancestor;
- *ours_out = ours;
- *theirs_out = theirs;
-
-done:
- return error;
-}
-
-static int checkout_conflicts_coalesce_renames(
- checkout_data *data)
-{
- git_index *index;
- const git_index_name_entry *name_entry;
- checkout_conflictdata *ancestor_conflict, *our_conflict, *their_conflict;
- size_t i, names;
- int error = 0;
-
- if ((index = git_iterator_index(data->target)) == NULL)
- return 0;
-
- /* Juggle entries based on renames */
- names = git_index_name_entrycount(index);
-
- for (i = 0; i < names; i++) {
- name_entry = git_index_name_get_byindex(index, i);
-
- if ((error = checkout_conflicts_load_byname_entry(
- &ancestor_conflict, &our_conflict, &their_conflict,
- data, name_entry)) < 0)
- goto done;
-
- if (our_conflict && our_conflict != ancestor_conflict) {
- ancestor_conflict->ours = our_conflict->ours;
- our_conflict->ours = NULL;
-
- if (our_conflict->theirs)
- our_conflict->name_collision = 1;
-
- if (our_conflict->name_collision)
- ancestor_conflict->name_collision = 1;
- }
-
- if (their_conflict && their_conflict != ancestor_conflict) {
- ancestor_conflict->theirs = their_conflict->theirs;
- their_conflict->theirs = NULL;
-
- if (their_conflict->ours)
- their_conflict->name_collision = 1;
-
- if (their_conflict->name_collision)
- ancestor_conflict->name_collision = 1;
- }
-
- if (our_conflict && our_conflict != ancestor_conflict &&
- their_conflict && their_conflict != ancestor_conflict)
- ancestor_conflict->one_to_two = 1;
- }
-
- git_vector_remove_matching(
- &data->update_conflicts, checkout_conflictdata_empty, NULL);
-
-done:
- return error;
-}
-
-static int checkout_conflicts_mark_directoryfile(
- checkout_data *data)
-{
- git_index *index;
- checkout_conflictdata *conflict;
- const git_index_entry *entry;
- size_t i, j, len;
- const char *path;
- int prefixed, error = 0;
-
- if ((index = git_iterator_index(data->target)) == NULL)
- return 0;
-
- len = git_index_entrycount(index);
-
- /* Find d/f conflicts */
- git_vector_foreach(&data->update_conflicts, i, conflict) {
- if ((conflict->ours && conflict->theirs) ||
- (!conflict->ours && !conflict->theirs))
- continue;
-
- path = conflict->ours ?
- conflict->ours->path : conflict->theirs->path;
-
- if ((error = git_index_find(&j, index, path)) < 0) {
- if (error == GIT_ENOTFOUND)
- giterr_set(GITERR_INDEX,
- "Index inconsistency, could not find entry for expected conflict '%s'", path);
-
- goto done;
- }
-
- for (; j < len; j++) {
- if ((entry = git_index_get_byindex(index, j)) == NULL) {
- giterr_set(GITERR_INDEX,
- "Index inconsistency, truncated index while loading expected conflict '%s'", path);
- error = -1;
- goto done;
- }
-
- prefixed = git_path_equal_or_prefixed(path, entry->path, NULL);
-
- if (prefixed == GIT_PATH_EQUAL)
- continue;
-
- if (prefixed == GIT_PATH_PREFIX)
- conflict->directoryfile = 1;
-
- break;
- }
- }
-
-done:
- return error;
-}
-
-static int checkout_get_update_conflicts(
- checkout_data *data,
- git_iterator *workdir,
- git_vector *pathspec)
-{
- int error = 0;
-
- if (data->strategy & GIT_CHECKOUT_SKIP_UNMERGED)
- return 0;
-
- if ((error = checkout_conflicts_load(data, workdir, pathspec)) < 0 ||
- (error = checkout_conflicts_coalesce_renames(data)) < 0 ||
- (error = checkout_conflicts_mark_directoryfile(data)) < 0)
- goto done;
-
-done:
- return error;
-}
-
-static int checkout_conflict_append_remove(
- const git_index_entry *ancestor,
- const git_index_entry *ours,
- const git_index_entry *theirs,
- void *payload)
-{
- checkout_data *data = payload;
- const char *name;
-
- assert(ancestor || ours || theirs);
-
- if (ancestor)
- name = git__strdup(ancestor->path);
- else if (ours)
- name = git__strdup(ours->path);
- else if (theirs)
- name = git__strdup(theirs->path);
- else
- abort();
-
- GITERR_CHECK_ALLOC(name);
-
- return git_vector_insert(&data->remove_conflicts, (char *)name);
-}
-
-static int checkout_get_remove_conflicts(
- checkout_data *data,
- git_iterator *workdir,
- git_vector *pathspec)
-{
- if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0)
- return 0;
-
- return checkout_conflicts_foreach(data, data->index, workdir, pathspec, checkout_conflict_append_remove, data);
-}
-
-static int checkout_verify_paths(
- git_repository *repo,
- int action,
- git_diff_delta *delta)
-{
- unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS;
-
- if (action & CHECKOUT_ACTION__REMOVE) {
- if (!git_path_isvalid(repo, delta->old_file.path, flags)) {
- giterr_set(GITERR_CHECKOUT, "Cannot remove invalid path '%s'", delta->old_file.path);
- return -1;
- }
- }
-
- if (action & ~CHECKOUT_ACTION__REMOVE) {
- if (!git_path_isvalid(repo, delta->new_file.path, flags)) {
- giterr_set(GITERR_CHECKOUT, "Cannot checkout to invalid path '%s'", delta->new_file.path);
- return -1;
- }
- }
-
- return 0;
-}
-
-static int checkout_get_actions(
- uint32_t **actions_ptr,
- size_t **counts_ptr,
- checkout_data *data,
- git_iterator *workdir)
-{
- int error = 0, act;
- const git_index_entry *wditem;
- git_vector pathspec = GIT_VECTOR_INIT, *deltas;
- git_pool pathpool;
- git_diff_delta *delta;
- size_t i, *counts = NULL;
- uint32_t *actions = NULL;
-
- git_pool_init(&pathpool, 1);
-
- if (data->opts.paths.count > 0 &&
- git_pathspec__vinit(&pathspec, &data->opts.paths, &pathpool) < 0)
- return -1;
-
- if ((error = git_iterator_current(&wditem, workdir)) < 0 &&
- error != GIT_ITEROVER)
- goto fail;
-
- deltas = &data->diff->deltas;
-
- *counts_ptr = counts = git__calloc(CHECKOUT_ACTION__MAX+1, sizeof(size_t));
- *actions_ptr = actions = git__calloc(
- deltas->length ? deltas->length : 1, sizeof(uint32_t));
- if (!counts || !actions) {
- error = -1;
- goto fail;
- }
-
- git_vector_foreach(deltas, i, delta) {
- if ((error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec)) == 0)
- error = checkout_verify_paths(data->repo, act, delta);
-
- if (error != 0)
- goto fail;
-
- actions[i] = act;
-
- if (act & CHECKOUT_ACTION__REMOVE)
- counts[CHECKOUT_ACTION__REMOVE]++;
- if (act & CHECKOUT_ACTION__UPDATE_BLOB)
- counts[CHECKOUT_ACTION__UPDATE_BLOB]++;
- if (act & CHECKOUT_ACTION__UPDATE_SUBMODULE)
- counts[CHECKOUT_ACTION__UPDATE_SUBMODULE]++;
- if (act & CHECKOUT_ACTION__CONFLICT)
- counts[CHECKOUT_ACTION__CONFLICT]++;
- }
-
- error = checkout_remaining_wd_items(data, workdir, wditem, &pathspec);
- if (error)
- goto fail;
-
- counts[CHECKOUT_ACTION__REMOVE] += data->removes.length;
-
- if (counts[CHECKOUT_ACTION__CONFLICT] > 0 &&
- (data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0)
- {
- giterr_set(GITERR_CHECKOUT, "%"PRIuZ" %s checkout",
- counts[CHECKOUT_ACTION__CONFLICT],
- counts[CHECKOUT_ACTION__CONFLICT] == 1 ?
- "conflict prevents" : "conflicts prevent");
- error = GIT_ECONFLICT;
- goto fail;
- }
-
-
- if ((error = checkout_get_remove_conflicts(data, workdir, &pathspec)) < 0 ||
- (error = checkout_get_update_conflicts(data, workdir, &pathspec)) < 0)
- goto fail;
-
- counts[CHECKOUT_ACTION__REMOVE_CONFLICT] = git_vector_length(&data->remove_conflicts);
- counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->update_conflicts);
-
- git_pathspec__vfree(&pathspec);
- git_pool_clear(&pathpool);
-
- return 0;
-
-fail:
- *counts_ptr = NULL;
- git__free(counts);
- *actions_ptr = NULL;
- git__free(actions);
-
- git_pathspec__vfree(&pathspec);
- git_pool_clear(&pathpool);
-
- return error;
-}
-
-static bool should_remove_existing(checkout_data *data)
-{
- int ignorecase;
-
- if (git_repository__cvar(&ignorecase, data->repo, GIT_CVAR_IGNORECASE) < 0) {
- ignorecase = 0;
- }
-
- return (ignorecase &&
- (data->strategy & GIT_CHECKOUT_DONT_REMOVE_EXISTING) == 0);
-}
-
-#define MKDIR_NORMAL \
- GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR
-#define MKDIR_REMOVE_EXISTING \
- MKDIR_NORMAL | GIT_MKDIR_REMOVE_FILES | GIT_MKDIR_REMOVE_SYMLINKS
-
-static int checkout_mkdir(
- checkout_data *data,
- const char *path,
- const char *base,
- mode_t mode,
- unsigned int flags)
-{
- struct git_futils_mkdir_options mkdir_opts = {0};
- int error;
-
- mkdir_opts.dir_map = data->mkdir_map;
- mkdir_opts.pool = &data->pool;
-
- error = git_futils_mkdir_relative(
- path, base, mode, flags, &mkdir_opts);
-
- data->perfdata.mkdir_calls += mkdir_opts.perfdata.mkdir_calls;
- data->perfdata.stat_calls += mkdir_opts.perfdata.stat_calls;
- data->perfdata.chmod_calls += mkdir_opts.perfdata.chmod_calls;
-
- return error;
-}
-
-static int mkpath2file(
- checkout_data *data, const char *path, unsigned int mode)
-{
- struct stat st;
- bool remove_existing = should_remove_existing(data);
- unsigned int flags =
- (remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL) |
- GIT_MKDIR_SKIP_LAST;
- int error;
-
- if ((error = checkout_mkdir(
- data, path, data->opts.target_directory, mode, flags)) < 0)
- return error;
-
- if (remove_existing) {
- data->perfdata.stat_calls++;
-
- if (p_lstat(path, &st) == 0) {
-
- /* Some file, symlink or folder already exists at this name.
- * We would have removed it in remove_the_old unless we're on
- * a case inensitive filesystem (or the user has asked us not
- * to). Remove the similarly named file to write the new.
- */
- error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES);
- } else if (errno != ENOENT) {
- giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
- return GIT_EEXISTS;
- } else {
- giterr_clear();
- }
- }
-
- return error;
-}
-
-struct checkout_stream {
- git_writestream base;
- const char *path;
- int fd;
- int open;
-};
-
-static int checkout_stream_write(
- git_writestream *s, const char *buffer, size_t len)
-{
- struct checkout_stream *stream = (struct checkout_stream *)s;
- int ret;
-
- if ((ret = p_write(stream->fd, buffer, len)) < 0)
- giterr_set(GITERR_OS, "Could not write to '%s'", stream->path);
-
- return ret;
-}
-
-static int checkout_stream_close(git_writestream *s)
-{
- struct checkout_stream *stream = (struct checkout_stream *)s;
- assert(stream && stream->open);
-
- stream->open = 0;
- return p_close(stream->fd);
-}
-
-static void checkout_stream_free(git_writestream *s)
-{
- GIT_UNUSED(s);
-}
-
-static int blob_content_to_file(
- checkout_data *data,
- struct stat *st,
- git_blob *blob,
- const char *path,
- const char *hint_path,
- mode_t entry_filemode)
-{
- int flags = data->opts.file_open_flags;
- mode_t file_mode = data->opts.file_mode ?
- data->opts.file_mode : entry_filemode;
- git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
- struct checkout_stream writer;
- mode_t mode;
- git_filter_list *fl = NULL;
- int fd;
- int error = 0;
-
- if (hint_path == NULL)
- hint_path = path;
-
- if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
- return error;
-
- if (flags <= 0)
- flags = O_CREAT | O_TRUNC | O_WRONLY;
- if (!(mode = file_mode))
- mode = GIT_FILEMODE_BLOB;
-
- if ((fd = p_open(path, flags, mode)) < 0) {
- giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
- return fd;
- }
-
- filter_opts.attr_session = &data->attr_session;
- filter_opts.temp_buf = &data->tmp;
-
- if (!data->opts.disable_filters &&
- (error = git_filter_list__load_ext(
- &fl, data->repo, blob, hint_path,
- GIT_FILTER_TO_WORKTREE, &filter_opts))) {
- p_close(fd);
- return error;
- }
-
- /* setup the writer */
- memset(&writer, 0, sizeof(struct checkout_stream));
- writer.base.write = checkout_stream_write;
- writer.base.close = checkout_stream_close;
- writer.base.free = checkout_stream_free;
- writer.path = path;
- writer.fd = fd;
- writer.open = 1;
-
- error = git_filter_list_stream_blob(fl, blob, &writer.base);
-
- assert(writer.open == 0);
-
- git_filter_list_free(fl);
-
- if (error < 0)
- return error;
-
- if (st) {
- data->perfdata.stat_calls++;
-
- if ((error = p_stat(path, st)) < 0) {
- giterr_set(GITERR_OS, "Error statting '%s'", path);
- return error;
- }
-
- st->st_mode = entry_filemode;
- }
-
- return 0;
-}
-
-static int blob_content_to_link(
- checkout_data *data,
- struct stat *st,
- git_blob *blob,
- const char *path)
-{
- git_buf linktarget = GIT_BUF_INIT;
- int error;
-
- if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
- return error;
-
- if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
- return error;
-
- if (data->can_symlink) {
- if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0)
- giterr_set(GITERR_OS, "Could not create symlink %s", path);
- } else {
- error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path);
- }
-
- if (!error) {
- data->perfdata.stat_calls++;
-
- if ((error = p_lstat(path, st)) < 0)
- giterr_set(GITERR_CHECKOUT, "Could not stat symlink %s", path);
-
- st->st_mode = GIT_FILEMODE_LINK;
- }
-
- git_buf_free(&linktarget);
-
- return error;
-}
-
-static int checkout_update_index(
- checkout_data *data,
- const git_diff_file *file,
- struct stat *st)
-{
- git_index_entry entry;
-
- if (!data->index)
- return 0;
-
- memset(&entry, 0, sizeof(entry));
- entry.path = (char *)file->path; /* cast to prevent warning */
- git_index_entry__init_from_stat(&entry, st, true);
- git_oid_cpy(&entry.id, &file->id);
-
- return git_index_add(data->index, &entry);
-}
-
-static int checkout_submodule_update_index(
- checkout_data *data,
- const git_diff_file *file)
-{
- git_buf *fullpath;
- struct stat st;
-
- /* update the index unless prevented */
- if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0)
- return 0;
-
- if (checkout_target_fullpath(&fullpath, data, file->path) < 0)
- return -1;
-
- data->perfdata.stat_calls++;
- if (p_stat(fullpath->ptr, &st) < 0) {
- giterr_set(
- GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
- return GIT_ENOTFOUND;
- }
-
- st.st_mode = GIT_FILEMODE_COMMIT;
-
- return checkout_update_index(data, file, &st);
-}
-
-static int checkout_submodule(
- checkout_data *data,
- const git_diff_file *file)
-{
- bool remove_existing = should_remove_existing(data);
- int error = 0;
-
- /* Until submodules are supported, UPDATE_ONLY means do nothing here */
- if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
- return 0;
-
- if ((error = checkout_mkdir(
- data,
- file->path, data->opts.target_directory, data->opts.dir_mode,
- remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL)) < 0)
- return error;
-
- if ((error = git_submodule_lookup(NULL, data->repo, file->path)) < 0) {
- /* I've observed repos with submodules in the tree that do not
- * have a .gitmodules - core Git just makes an empty directory
- */
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- return checkout_submodule_update_index(data, file);
- }
-
- return error;
- }
-
- /* TODO: Support checkout_strategy options. Two circumstances:
- * 1 - submodule already checked out, but we need to move the HEAD
- * to the new OID, or
- * 2 - submodule not checked out and we should recursively check it out
- *
- * Checkout will not execute a pull on the submodule, but a clone
- * command should probably be able to. Do we need a submodule callback?
- */
-
- return checkout_submodule_update_index(data, file);
-}
-
-static void report_progress(
- checkout_data *data,
- const char *path)
-{
- if (data->opts.progress_cb)
- data->opts.progress_cb(
- path, data->completed_steps, data->total_steps,
- data->opts.progress_payload);
-}
-
-static int checkout_safe_for_update_only(
- checkout_data *data, const char *path, mode_t expected_mode)
-{
- struct stat st;
-
- data->perfdata.stat_calls++;
-
- if (p_lstat(path, &st) < 0) {
- /* if doesn't exist, then no error and no update */
- if (errno == ENOENT || errno == ENOTDIR)
- return 0;
-
- /* otherwise, stat error and no update */
- giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
- return -1;
- }
-
- /* only safe for update if this is the same type of file */
- if ((st.st_mode & ~0777) == (expected_mode & ~0777))
- return 1;
-
- return 0;
-}
-
-static int checkout_write_content(
- checkout_data *data,
- const git_oid *oid,
- const char *full_path,
- const char *hint_path,
- unsigned int mode,
- struct stat *st)
-{
- int error = 0;
- git_blob *blob;
-
- if ((error = git_blob_lookup(&blob, data->repo, oid)) < 0)
- return error;
-
- if (S_ISLNK(mode))
- error = blob_content_to_link(data, st, blob, full_path);
- else
- error = blob_content_to_file(data, st, blob, full_path, hint_path, mode);
-
- git_blob_free(blob);
-
- /* if we try to create the blob and an existing directory blocks it from
- * being written, then there must have been a typechange conflict in a
- * parent directory - suppress the error and try to continue.
- */
- if ((data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) != 0 &&
- (error == GIT_ENOTFOUND || error == GIT_EEXISTS))
- {
- giterr_clear();
- error = 0;
- }
-
- return error;
-}
-
-static int checkout_blob(
- checkout_data *data,
- const git_diff_file *file)
-{
- git_buf *fullpath;
- struct stat st;
- int error = 0;
-
- if (checkout_target_fullpath(&fullpath, data, file->path) < 0)
- return -1;
-
- if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
- int rval = checkout_safe_for_update_only(
- data, fullpath->ptr, file->mode);
-
- if (rval <= 0)
- return rval;
- }
-
- error = checkout_write_content(
- data, &file->id, fullpath->ptr, NULL, file->mode, &st);
-
- /* update the index unless prevented */
- if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
- error = checkout_update_index(data, file, &st);
-
- /* update the submodule data if this was a new .gitmodules file */
- if (!error && strcmp(file->path, ".gitmodules") == 0)
- data->reload_submodules = true;
-
- return error;
-}
-
-static int checkout_remove_the_old(
- unsigned int *actions,
- checkout_data *data)
-{
- int error = 0;
- git_diff_delta *delta;
- const char *str;
- size_t i;
- git_buf *fullpath;
- uint32_t flg = GIT_RMDIR_EMPTY_PARENTS |
- GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS;
-
- if (data->opts.checkout_strategy & GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES)
- flg |= GIT_RMDIR_SKIP_NONEMPTY;
-
- if (checkout_target_fullpath(&fullpath, data, NULL) < 0)
- return -1;
-
- git_vector_foreach(&data->diff->deltas, i, delta) {
- if (actions[i] & CHECKOUT_ACTION__REMOVE) {
- error = git_futils_rmdir_r(
- delta->old_file.path, fullpath->ptr, flg);
-
- if (error < 0)
- return error;
-
- data->completed_steps++;
- report_progress(data, delta->old_file.path);
-
- if ((actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) == 0 &&
- (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 &&
- data->index != NULL)
- {
- (void)git_index_remove(data->index, delta->old_file.path, 0);
- }
- }
- }
-
- git_vector_foreach(&data->removes, i, str) {
- error = git_futils_rmdir_r(str, fullpath->ptr, flg);
- if (error < 0)
- return error;
-
- data->completed_steps++;
- report_progress(data, str);
-
- if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 &&
- data->index != NULL)
- {
- if (str[strlen(str) - 1] == '/')
- (void)git_index_remove_directory(data->index, str, 0);
- else
- (void)git_index_remove(data->index, str, 0);
- }
- }
-
- return 0;
-}
-
-static int checkout_deferred_remove(git_repository *repo, const char *path)
-{
-#if 0
- int error = git_futils_rmdir_r(
- path, data->opts.target_directory, GIT_RMDIR_EMPTY_PARENTS);
-
- if (error == GIT_ENOTFOUND) {
- error = 0;
- giterr_clear();
- }
-
- return error;
-#else
- GIT_UNUSED(repo);
- GIT_UNUSED(path);
- assert(false);
- return 0;
-#endif
-}
-
-static int checkout_create_the_new(
- unsigned int *actions,
- checkout_data *data)
-{
- int error = 0;
- git_diff_delta *delta;
- size_t i;
-
- git_vector_foreach(&data->diff->deltas, i, delta) {
- if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
- /* this had a blocker directory that should only be removed iff
- * all of the contents of the directory were safely removed
- */
- if ((error = checkout_deferred_remove(
- data->repo, delta->old_file.path)) < 0)
- return error;
- }
-
- if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) {
- error = checkout_blob(data, &delta->new_file);
- if (error < 0)
- return error;
-
- data->completed_steps++;
- report_progress(data, delta->new_file.path);
- }
- }
-
- return 0;
-}
-
-static int checkout_create_submodules(
- unsigned int *actions,
- checkout_data *data)
-{
- int error = 0;
- git_diff_delta *delta;
- size_t i;
-
- git_vector_foreach(&data->diff->deltas, i, delta) {
- if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
- /* this has a blocker directory that should only be removed iff
- * all of the contents of the directory were safely removed
- */
- if ((error = checkout_deferred_remove(
- data->repo, delta->old_file.path)) < 0)
- return error;
- }
-
- if (actions[i] & CHECKOUT_ACTION__UPDATE_SUBMODULE) {
- int error = checkout_submodule(data, &delta->new_file);
- if (error < 0)
- return error;
-
- data->completed_steps++;
- report_progress(data, delta->new_file.path);
- }
- }
-
- return 0;
-}
-
-static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)
-{
- int error = 0;
- git_reference *ref = NULL;
- git_object *head;
-
- if (!(error = git_repository_head(&ref, repo)) &&
- !(error = git_reference_peel(&head, ref, GIT_OBJ_TREE)))
- *out = (git_tree *)head;
-
- git_reference_free(ref);
-
- return error;
-}
-
-
-static int conflict_entry_name(
- git_buf *out,
- const char *side_name,
- const char *filename)
-{
- if (git_buf_puts(out, side_name) < 0 ||
- git_buf_putc(out, ':') < 0 ||
- git_buf_puts(out, filename) < 0)
- return -1;
-
- return 0;
-}
-
-static int checkout_path_suffixed(git_buf *path, const char *suffix)
-{
- size_t path_len;
- int i = 0, error = 0;
-
- if ((error = git_buf_putc(path, '~')) < 0 || (error = git_buf_puts(path, suffix)) < 0)
- return -1;
-
- path_len = git_buf_len(path);
-
- while (git_path_exists(git_buf_cstr(path)) && i < INT_MAX) {
- git_buf_truncate(path, path_len);
-
- if ((error = git_buf_putc(path, '_')) < 0 ||
- (error = git_buf_printf(path, "%d", i)) < 0)
- return error;
-
- i++;
- }
-
- if (i == INT_MAX) {
- git_buf_truncate(path, path_len);
-
- giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path->ptr);
- return GIT_EEXISTS;
- }
-
- return 0;
-}
-
-static int checkout_write_entry(
- checkout_data *data,
- checkout_conflictdata *conflict,
- const git_index_entry *side)
-{
- const char *hint_path = NULL, *suffix;
- git_buf *fullpath;
- struct stat st;
- int error;
-
- assert (side == conflict->ours || side == conflict->theirs);
-
- if (checkout_target_fullpath(&fullpath, data, side->path) < 0)
- return -1;
-
- if ((conflict->name_collision || conflict->directoryfile) &&
- (data->strategy & GIT_CHECKOUT_USE_OURS) == 0 &&
- (data->strategy & GIT_CHECKOUT_USE_THEIRS) == 0) {
-
- if (side == conflict->ours)
- suffix = data->opts.our_label ? data->opts.our_label :
- "ours";
- else
- suffix = data->opts.their_label ? data->opts.their_label :
- "theirs";
-
- if (checkout_path_suffixed(fullpath, suffix) < 0)
- return -1;
-
- hint_path = side->path;
- }
-
- if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
- (error = checkout_safe_for_update_only(data, fullpath->ptr, side->mode)) <= 0)
- return error;
-
- return checkout_write_content(data,
- &side->id, fullpath->ptr, hint_path, side->mode, &st);
-}
-
-static int checkout_write_entries(
- checkout_data *data,
- checkout_conflictdata *conflict)
-{
- int error = 0;
-
- if ((error = checkout_write_entry(data, conflict, conflict->ours)) >= 0)
- error = checkout_write_entry(data, conflict, conflict->theirs);
-
- return error;
-}
-
-static int checkout_merge_path(
- git_buf *out,
- checkout_data *data,
- checkout_conflictdata *conflict,
- git_merge_file_result *result)
-{
- const char *our_label_raw, *their_label_raw, *suffix;
- int error = 0;
-
- if ((error = git_buf_joinpath(out, git_repository_workdir(data->repo), result->path)) < 0)
- return error;
-
- /* Most conflicts simply use the filename in the index */
- if (!conflict->name_collision)
- return 0;
-
- /* Rename 2->1 conflicts need the branch name appended */
- our_label_raw = data->opts.our_label ? data->opts.our_label : "ours";
- their_label_raw = data->opts.their_label ? data->opts.their_label : "theirs";
- suffix = strcmp(result->path, conflict->ours->path) == 0 ? our_label_raw : their_label_raw;
-
- if ((error = checkout_path_suffixed(out, suffix)) < 0)
- return error;
-
- return 0;
-}
-
-static int checkout_write_merge(
- checkout_data *data,
- checkout_conflictdata *conflict)
-{
- git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT,
- path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT,
- in_data = GIT_BUF_INIT, out_data = GIT_BUF_INIT;
- git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
- git_merge_file_result result = {0};
- git_filebuf output = GIT_FILEBUF_INIT;
- git_filter_list *fl = NULL;
- git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
- int error = 0;
-
- if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)
- opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3;
-
- opts.ancestor_label = data->opts.ancestor_label ?
- data->opts.ancestor_label : "ancestor";
- opts.our_label = data->opts.our_label ?
- data->opts.our_label : "ours";
- opts.their_label = data->opts.their_label ?
- data->opts.their_label : "theirs";
-
- /* If all the paths are identical, decorate the diff3 file with the branch
- * names. Otherwise, append branch_name:path.
- */
- if (conflict->ours && conflict->theirs &&
- strcmp(conflict->ours->path, conflict->theirs->path) != 0) {
-
- if ((error = conflict_entry_name(
- &our_label, opts.our_label, conflict->ours->path)) < 0 ||
- (error = conflict_entry_name(
- &their_label, opts.their_label, conflict->theirs->path)) < 0)
- goto done;
-
- opts.our_label = git_buf_cstr(&our_label);
- opts.their_label = git_buf_cstr(&their_label);
- }
-
- if ((error = git_merge_file_from_index(&result, data->repo,
- conflict->ancestor, conflict->ours, conflict->theirs, &opts)) < 0)
- goto done;
-
- if (result.path == NULL || result.mode == 0) {
- giterr_set(GITERR_CHECKOUT, "Could not merge contents of file");
- error = GIT_ECONFLICT;
- goto done;
- }
-
- if ((error = checkout_merge_path(&path_workdir, data, conflict, &result)) < 0)
- goto done;
-
- if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
- (error = checkout_safe_for_update_only(data, git_buf_cstr(&path_workdir), result.mode)) <= 0)
- goto done;
-
- if (!data->opts.disable_filters) {
- in_data.ptr = (char *)result.ptr;
- in_data.size = result.len;
-
- filter_opts.attr_session = &data->attr_session;
- filter_opts.temp_buf = &data->tmp;
-
- if ((error = git_filter_list__load_ext(
- &fl, data->repo, NULL, git_buf_cstr(&path_workdir),
- GIT_FILTER_TO_WORKTREE, &filter_opts)) < 0 ||
- (error = git_filter_list_apply_to_data(&out_data, fl, &in_data)) < 0)
- goto done;
- } else {
- out_data.ptr = (char *)result.ptr;
- out_data.size = result.len;
- }
-
- if ((error = mkpath2file(data, path_workdir.ptr, data->opts.dir_mode)) < 0 ||
- (error = git_filebuf_open(&output, git_buf_cstr(&path_workdir), GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 ||
- (error = git_filebuf_write(&output, out_data.ptr, out_data.size)) < 0 ||
- (error = git_filebuf_commit(&output)) < 0)
- goto done;
-
-done:
- git_filter_list_free(fl);
-
- git_buf_free(&out_data);
- git_buf_free(&our_label);
- git_buf_free(&their_label);
-
- git_merge_file_result_free(&result);
- git_buf_free(&path_workdir);
- git_buf_free(&path_suffixed);
-
- return error;
-}
-
-static int checkout_conflict_add(
- checkout_data *data,
- const git_index_entry *conflict)
-{
- int error = git_index_remove(data->index, conflict->path, 0);
-
- if (error == GIT_ENOTFOUND)
- giterr_clear();
- else if (error < 0)
- return error;
-
- return git_index_add(data->index, conflict);
-}
-
-static int checkout_conflict_update_index(
- checkout_data *data,
- checkout_conflictdata *conflict)
-{
- int error = 0;
-
- if (conflict->ancestor)
- error = checkout_conflict_add(data, conflict->ancestor);
-
- if (!error && conflict->ours)
- error = checkout_conflict_add(data, conflict->ours);
-
- if (!error && conflict->theirs)
- error = checkout_conflict_add(data, conflict->theirs);
-
- return error;
-}
-
-static int checkout_create_conflicts(checkout_data *data)
-{
- checkout_conflictdata *conflict;
- size_t i;
- int error = 0;
-
- git_vector_foreach(&data->update_conflicts, i, conflict) {
-
- /* Both deleted: nothing to do */
- if (conflict->ours == NULL && conflict->theirs == NULL)
- error = 0;
-
- else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
- conflict->ours)
- error = checkout_write_entry(data, conflict, conflict->ours);
- else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
- conflict->theirs)
- error = checkout_write_entry(data, conflict, conflict->theirs);
-
- /* Ignore the other side of name collisions. */
- else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
- !conflict->ours && conflict->name_collision)
- error = 0;
- else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
- !conflict->theirs && conflict->name_collision)
- error = 0;
-
- /* For modify/delete, name collisions and d/f conflicts, write
- * the file (potentially with the name mangled.
- */
- else if (conflict->ours != NULL && conflict->theirs == NULL)
- error = checkout_write_entry(data, conflict, conflict->ours);
- else if (conflict->ours == NULL && conflict->theirs != NULL)
- error = checkout_write_entry(data, conflict, conflict->theirs);
-
- /* Add/add conflicts and rename 1->2 conflicts, write the
- * ours/theirs sides (potentially name mangled).
- */
- else if (conflict->one_to_two)
- error = checkout_write_entries(data, conflict);
-
- /* If all sides are links, write the ours side */
- else if (S_ISLNK(conflict->ours->mode) &&
- S_ISLNK(conflict->theirs->mode))
- error = checkout_write_entry(data, conflict, conflict->ours);
- /* Link/file conflicts, write the file side */
- else if (S_ISLNK(conflict->ours->mode))
- error = checkout_write_entry(data, conflict, conflict->theirs);
- else if (S_ISLNK(conflict->theirs->mode))
- error = checkout_write_entry(data, conflict, conflict->ours);
-
- /* If any side is a gitlink, do nothing. */
- else if (conflict->submodule)
- error = 0;
-
- /* If any side is binary, write the ours side */
- else if (conflict->binary)
- error = checkout_write_entry(data, conflict, conflict->ours);
-
- else if (!error)
- error = checkout_write_merge(data, conflict);
-
- /* Update the index extensions (REUC and NAME) if we're checking
- * out a different index. (Otherwise just leave them there.)
- */
- if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
- error = checkout_conflict_update_index(data, conflict);
-
- if (error)
- break;
-
- data->completed_steps++;
- report_progress(data,
- conflict->ours ? conflict->ours->path :
- (conflict->theirs ? conflict->theirs->path : conflict->ancestor->path));
- }
-
- return error;
-}
-
-static int checkout_remove_conflicts(checkout_data *data)
-{
- const char *conflict;
- size_t i;
-
- git_vector_foreach(&data->remove_conflicts, i, conflict) {
- if (git_index_conflict_remove(data->index, conflict) < 0)
- return -1;
-
- data->completed_steps++;
- }
-
- return 0;
-}
-
-static int checkout_extensions_update_index(checkout_data *data)
-{
- const git_index_reuc_entry *reuc_entry;
- const git_index_name_entry *name_entry;
- size_t i;
- int error = 0;
-
- if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
- return 0;
-
- if (data->update_reuc) {
- git_vector_foreach(data->update_reuc, i, reuc_entry) {
- if ((error = git_index_reuc_add(data->index, reuc_entry->path,
- reuc_entry->mode[0], &reuc_entry->oid[0],
- reuc_entry->mode[1], &reuc_entry->oid[1],
- reuc_entry->mode[2], &reuc_entry->oid[2])) < 0)
- goto done;
- }
- }
-
- if (data->update_names) {
- git_vector_foreach(data->update_names, i, name_entry) {
- if ((error = git_index_name_add(data->index, name_entry->ancestor,
- name_entry->ours, name_entry->theirs)) < 0)
- goto done;
- }
- }
-
-done:
- return error;
-}
-
-static void checkout_data_clear(checkout_data *data)
-{
- if (data->opts_free_baseline) {
- git_tree_free(data->opts.baseline);
- data->opts.baseline = NULL;
- }
-
- git_vector_free(&data->removes);
- git_pool_clear(&data->pool);
-
- git_vector_free_deep(&data->remove_conflicts);
- git_vector_free_deep(&data->update_conflicts);
-
- git__free(data->pfx);
- data->pfx = NULL;
-
- git_strmap_free(data->mkdir_map);
-
- git_buf_free(&data->target_path);
- git_buf_free(&data->tmp);
-
- git_index_free(data->index);
- data->index = NULL;
-
- git_strmap_free(data->mkdir_map);
-
- git_attr_session__free(&data->attr_session);
-}
-
-static int checkout_data_init(
- checkout_data *data,
- git_iterator *target,
- const git_checkout_options *proposed)
-{
- int error = 0;
- git_repository *repo = git_iterator_owner(target);
-
- memset(data, 0, sizeof(*data));
-
- if (!repo) {
- giterr_set(GITERR_CHECKOUT, "Cannot checkout nothing");
- return -1;
- }
-
- if ((!proposed || !proposed->target_directory) &&
- (error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
- return error;
-
- data->repo = repo;
- data->target = target;
-
- GITERR_CHECK_VERSION(
- proposed, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options");
-
- if (!proposed)
- GIT_INIT_STRUCTURE(&data->opts, GIT_CHECKOUT_OPTIONS_VERSION);
- else
- memmove(&data->opts, proposed, sizeof(git_checkout_options));
-
- if (!data->opts.target_directory)
- data->opts.target_directory = git_repository_workdir(repo);
- else if (!git_path_isdir(data->opts.target_directory) &&
- (error = checkout_mkdir(data,
- data->opts.target_directory, NULL,
- GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0)
- goto cleanup;
-
- /* refresh config and index content unless NO_REFRESH is given */
- if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
- git_config *cfg;
-
- if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
- goto cleanup;
-
- /* Get the repository index and reload it (unless we're checking
- * out the index; then it has the changes we're trying to check
- * out and those should not be overwritten.)
- */
- if ((error = git_repository_index(&data->index, data->repo)) < 0)
- goto cleanup;
-
- if (data->index != git_iterator_index(target)) {
- if ((error = git_index_read(data->index, true)) < 0)
- goto cleanup;
-
- /* cannot checkout if unresolved conflicts exist */
- if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) == 0 &&
- git_index_has_conflicts(data->index)) {
- error = GIT_ECONFLICT;
- giterr_set(GITERR_CHECKOUT,
- "unresolved conflicts exist in the index");
- goto cleanup;
- }
-
- /* clean conflict data in the current index */
- git_index_name_clear(data->index);
- git_index_reuc_clear(data->index);
- }
- }
-
- /* if you are forcing, allow all safe updates, plus recreate missing */
- if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) != 0)
- data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE |
- GIT_CHECKOUT_RECREATE_MISSING;
-
- /* if the repository does not actually have an index file, then this
- * is an initial checkout (perhaps from clone), so we allow safe updates
- */
- if (!data->index->on_disk &&
- (data->opts.checkout_strategy & GIT_CHECKOUT_SAFE) != 0)
- data->opts.checkout_strategy |= GIT_CHECKOUT_RECREATE_MISSING;
-
- data->strategy = data->opts.checkout_strategy;
-
- /* opts->disable_filters is false by default */
-
- if (!data->opts.dir_mode)
- data->opts.dir_mode = GIT_DIR_MODE;
-
- if (!data->opts.file_open_flags)
- data->opts.file_open_flags = O_CREAT | O_TRUNC | O_WRONLY;
-
- data->pfx = git_pathspec_prefix(&data->opts.paths);
-
- if ((error = git_repository__cvar(
- &data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0)
- goto cleanup;
-
- if (!data->opts.baseline && !data->opts.baseline_index) {
- data->opts_free_baseline = true;
- error = 0;
-
- /* if we don't have an index, this is an initial checkout and
- * should be against an empty baseline
- */
- if (data->index->on_disk)
- error = checkout_lookup_head_tree(&data->opts.baseline, repo);
-
- if (error == GIT_EUNBORNBRANCH) {
- error = 0;
- giterr_clear();
- }
-
- if (error < 0)
- goto cleanup;
- }
-
- if ((data->opts.checkout_strategy &
- (GIT_CHECKOUT_CONFLICT_STYLE_MERGE | GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)) == 0) {
- git_config_entry *conflict_style = NULL;
- git_config *cfg = NULL;
-
- if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 ||
- (error = git_config_get_entry(&conflict_style, cfg, "merge.conflictstyle")) < 0 ||
- error == GIT_ENOTFOUND)
- ;
- else if (error)
- goto cleanup;
- else if (strcmp(conflict_style->value, "merge") == 0)
- data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_MERGE;
- else if (strcmp(conflict_style->value, "diff3") == 0)
- data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3;
- else {
- giterr_set(GITERR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'",
- conflict_style->value);
- error = -1;
- git_config_entry_free(conflict_style);
- goto cleanup;
- }
- git_config_entry_free(conflict_style);
- }
-
- git_pool_init(&data->pool, 1);
-
- if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
- (error = git_vector_init(&data->remove_conflicts, 0, NULL)) < 0 ||
- (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 ||
- (error = git_buf_puts(&data->target_path, data->opts.target_directory)) < 0 ||
- (error = git_path_to_dir(&data->target_path)) < 0 ||
- (error = git_strmap_alloc(&data->mkdir_map)) < 0)
- goto cleanup;
-
- data->target_len = git_buf_len(&data->target_path);
-
- git_attr_session__init(&data->attr_session, data->repo);
-
-cleanup:
- if (error < 0)
- checkout_data_clear(data);
-
- return error;
-}
-
-#define CHECKOUT_INDEX_DONT_WRITE_MASK \
- (GIT_CHECKOUT_DONT_UPDATE_INDEX | GIT_CHECKOUT_DONT_WRITE_INDEX)
-
-int git_checkout_iterator(
- git_iterator *target,
- git_index *index,
- const git_checkout_options *opts)
-{
- int error = 0;
- git_iterator *baseline = NULL, *workdir = NULL;
- git_iterator_options baseline_opts = GIT_ITERATOR_OPTIONS_INIT,
- workdir_opts = GIT_ITERATOR_OPTIONS_INIT;
- checkout_data data = {0};
- git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
- uint32_t *actions = NULL;
- size_t *counts = NULL;
-
- /* initialize structures and options */
- error = checkout_data_init(&data, target, opts);
- if (error < 0)
- return error;
-
- diff_opts.flags =
- GIT_DIFF_INCLUDE_UNMODIFIED |
- GIT_DIFF_INCLUDE_UNREADABLE |
- GIT_DIFF_INCLUDE_UNTRACKED |
- GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */
- GIT_DIFF_INCLUDE_IGNORED |
- GIT_DIFF_INCLUDE_TYPECHANGE |
- GIT_DIFF_INCLUDE_TYPECHANGE_TREES |
- GIT_DIFF_SKIP_BINARY_CHECK |
- GIT_DIFF_INCLUDE_CASECHANGE;
- if (data.opts.checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)
- diff_opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
- if (data.opts.paths.count > 0)
- diff_opts.pathspec = data.opts.paths;
-
- /* set up iterators */
-
- workdir_opts.flags = git_iterator_ignore_case(target) ?
- GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
- workdir_opts.flags |= GIT_ITERATOR_DONT_AUTOEXPAND;
- workdir_opts.start = data.pfx;
- workdir_opts.end = data.pfx;
-
- if ((error = git_iterator_reset_range(target, data.pfx, data.pfx)) < 0 ||
- (error = git_iterator_for_workdir_ext(
- &workdir, data.repo, data.opts.target_directory, index, NULL,
- &workdir_opts)) < 0)
- goto cleanup;
-
- baseline_opts.flags = git_iterator_ignore_case(target) ?
- GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
- baseline_opts.start = data.pfx;
- baseline_opts.end = data.pfx;
-
- if (data.opts.baseline_index) {
- if ((error = git_iterator_for_index(
- &baseline, git_index_owner(data.opts.baseline_index),
- data.opts.baseline_index, &baseline_opts)) < 0)
- goto cleanup;
- } else {
- if ((error = git_iterator_for_tree(
- &baseline, data.opts.baseline, &baseline_opts)) < 0)
- goto cleanup;
- }
-
- /* Should not have case insensitivity mismatch */
- assert(git_iterator_ignore_case(workdir) == git_iterator_ignore_case(baseline));
-
- /* Generate baseline-to-target diff which will include an entry for
- * every possible update that might need to be made.
- */
- if ((error = git_diff__from_iterators(
- &data.diff, data.repo, baseline, target, &diff_opts)) < 0)
- goto cleanup;
-
- /* Loop through diff (and working directory iterator) building a list of
- * actions to be taken, plus look for conflicts and send notifications,
- * then loop through conflicts.
- */
- if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) != 0)
- goto cleanup;
-
- data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
- counts[CHECKOUT_ACTION__REMOVE_CONFLICT] +
- counts[CHECKOUT_ACTION__UPDATE_BLOB] +
- counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] +
- counts[CHECKOUT_ACTION__UPDATE_CONFLICT];
-
- report_progress(&data, NULL); /* establish 0 baseline */
-
- /* To deal with some order dependencies, perform remaining checkout
- * in three passes: removes, then update blobs, then update submodules.
- */
- if (counts[CHECKOUT_ACTION__REMOVE] > 0 &&
- (error = checkout_remove_the_old(actions, &data)) < 0)
- goto cleanup;
-
- if (counts[CHECKOUT_ACTION__REMOVE_CONFLICT] > 0 &&
- (error = checkout_remove_conflicts(&data)) < 0)
- goto cleanup;
-
- if (counts[CHECKOUT_ACTION__UPDATE_BLOB] > 0 &&
- (error = checkout_create_the_new(actions, &data)) < 0)
- goto cleanup;
-
- if (counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] > 0 &&
- (error = checkout_create_submodules(actions, &data)) < 0)
- goto cleanup;
-
- if (counts[CHECKOUT_ACTION__UPDATE_CONFLICT] > 0 &&
- (error = checkout_create_conflicts(&data)) < 0)
- goto cleanup;
-
- if (data.index != git_iterator_index(target) &&
- (error = checkout_extensions_update_index(&data)) < 0)
- goto cleanup;
-
- assert(data.completed_steps == data.total_steps);
-
- if (data.opts.perfdata_cb)
- data.opts.perfdata_cb(&data.perfdata, data.opts.perfdata_payload);
-
-cleanup:
- if (!error && data.index != NULL &&
- (data.strategy & CHECKOUT_INDEX_DONT_WRITE_MASK) == 0)
- error = git_index_write(data.index);
-
- git_diff_free(data.diff);
- git_iterator_free(workdir);
- git_iterator_free(baseline);
- git__free(actions);
- git__free(counts);
- checkout_data_clear(&data);
-
- return error;
-}
-
-int git_checkout_index(
- git_repository *repo,
- git_index *index,
- const git_checkout_options *opts)
-{
- int error, owned = 0;
- git_iterator *index_i;
-
- if (!index && !repo) {
- giterr_set(GITERR_CHECKOUT,
- "Must provide either repository or index to checkout");
- return -1;
- }
-
- if (index && repo &&
- git_index_owner(index) &&
- git_index_owner(index) != repo) {
- giterr_set(GITERR_CHECKOUT,
- "Index to checkout does not match repository");
- return -1;
- } else if(index && repo && !git_index_owner(index)) {
- GIT_REFCOUNT_OWN(index, repo);
- owned = 1;
- }
-
- if (!repo)
- repo = git_index_owner(index);
-
- if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
- return error;
- GIT_REFCOUNT_INC(index);
-
- if (!(error = git_iterator_for_index(&index_i, repo, index, NULL)))
- error = git_checkout_iterator(index_i, index, opts);
-
- if (owned)
- GIT_REFCOUNT_OWN(index, NULL);
-
- git_iterator_free(index_i);
- git_index_free(index);
-
- return error;
-}
-
-int git_checkout_tree(
- git_repository *repo,
- const git_object *treeish,
- const git_checkout_options *opts)
-{
- int error;
- git_index *index;
- git_tree *tree = NULL;
- git_iterator *tree_i = NULL;
- git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
-
- if (!treeish && !repo) {
- giterr_set(GITERR_CHECKOUT,
- "Must provide either repository or tree to checkout");
- return -1;
- }
- if (treeish && repo && git_object_owner(treeish) != repo) {
- giterr_set(GITERR_CHECKOUT,
- "Object to checkout does not match repository");
- return -1;
- }
-
- if (!repo)
- repo = git_object_owner(treeish);
-
- if (treeish) {
- if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
- giterr_set(
- GITERR_CHECKOUT, "Provided object cannot be peeled to a tree");
- return -1;
- }
- }
- else {
- if ((error = checkout_lookup_head_tree(&tree, repo)) < 0) {
- if (error != GIT_EUNBORNBRANCH)
- giterr_set(
- GITERR_CHECKOUT,
- "HEAD could not be peeled to a tree and no treeish given");
- return error;
- }
- }
-
- if ((error = git_repository_index(&index, repo)) < 0)
- return error;
-
- if (opts && (opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) {
- iter_opts.pathlist.count = opts->paths.count;
- iter_opts.pathlist.strings = opts->paths.strings;
- }
-
- if (!(error = git_iterator_for_tree(&tree_i, tree, &iter_opts)))
- error = git_checkout_iterator(tree_i, index, opts);
-
- git_iterator_free(tree_i);
- git_index_free(index);
- git_tree_free(tree);
-
- return error;
-}
-
-int git_checkout_head(
- git_repository *repo,
- const git_checkout_options *opts)
-{
- assert(repo);
- return git_checkout_tree(repo, NULL, opts);
-}
-
-int git_checkout_init_options(git_checkout_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_checkout_options, GIT_CHECKOUT_OPTIONS_INIT);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_checkout_h__
-#define INCLUDE_checkout_h__
-
-#include "git2/checkout.h"
-#include "iterator.h"
-
-#define GIT_CHECKOUT__NOTIFY_CONFLICT_TREE (1u << 12)
-
-/**
- * Update the working directory to match the target iterator. The
- * expected baseline value can be passed in via the checkout options
- * or else will default to the HEAD commit.
- */
-extern int git_checkout_iterator(
- git_iterator *target,
- git_index *index,
- const git_checkout_options *opts);
-
-#endif
+++ /dev/null
-/*
-* Copyright (C) the libgit2 contributors. All rights reserved.
-*
-* This file is part of libgit2, distributed under the GNU GPL v2 with
-* a Linking Exception. For full terms see the included COPYING file.
-*/
-
-#include "common.h"
-#include "repository.h"
-#include "filebuf.h"
-#include "merge.h"
-#include "vector.h"
-#include "index.h"
-
-#include "git2/types.h"
-#include "git2/merge.h"
-#include "git2/cherrypick.h"
-#include "git2/commit.h"
-#include "git2/sys/commit.h"
-
-#define GIT_CHERRYPICK_FILE_MODE 0666
-
-static int write_cherrypick_head(
- git_repository *repo,
- const char *commit_oidstr)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf file_path = GIT_BUF_INIT;
- int error = 0;
-
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_CHERRYPICK_HEAD_FILE)) >= 0 &&
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) >= 0 &&
- (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0)
- error = git_filebuf_commit(&file);
-
- if (error < 0)
- git_filebuf_cleanup(&file);
-
- git_buf_free(&file_path);
-
- return error;
-}
-
-static int write_merge_msg(
- git_repository *repo,
- const char *commit_msg)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf file_path = GIT_BUF_INIT;
- int error = 0;
-
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) < 0 ||
- (error = git_filebuf_printf(&file, "%s", commit_msg)) < 0)
- goto cleanup;
-
- error = git_filebuf_commit(&file);
-
-cleanup:
- if (error < 0)
- git_filebuf_cleanup(&file);
-
- git_buf_free(&file_path);
-
- return error;
-}
-
-static int cherrypick_normalize_opts(
- git_repository *repo,
- git_cherrypick_options *opts,
- const git_cherrypick_options *given,
- const char *their_label)
-{
- int error = 0;
- unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE |
- GIT_CHECKOUT_ALLOW_CONFLICTS;
-
- GIT_UNUSED(repo);
-
- if (given != NULL)
- memcpy(opts, given, sizeof(git_cherrypick_options));
- else {
- git_cherrypick_options default_opts = GIT_CHERRYPICK_OPTIONS_INIT;
- memcpy(opts, &default_opts, sizeof(git_cherrypick_options));
- }
-
- if (!opts->checkout_opts.checkout_strategy)
- opts->checkout_opts.checkout_strategy = default_checkout_strategy;
-
- if (!opts->checkout_opts.our_label)
- opts->checkout_opts.our_label = "HEAD";
-
- if (!opts->checkout_opts.their_label)
- opts->checkout_opts.their_label = their_label;
-
- return error;
-}
-
-static int cherrypick_state_cleanup(git_repository *repo)
-{
- const char *state_files[] = { GIT_CHERRYPICK_HEAD_FILE, GIT_MERGE_MSG_FILE };
-
- return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
-}
-
-static int cherrypick_seterr(git_commit *commit, const char *fmt)
-{
- char commit_oidstr[GIT_OID_HEXSZ + 1];
-
- giterr_set(GITERR_CHERRYPICK, fmt,
- git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit)));
-
- return -1;
-}
-
-int git_cherrypick_commit(
- git_index **out,
- git_repository *repo,
- git_commit *cherrypick_commit,
- git_commit *our_commit,
- unsigned int mainline,
- const git_merge_options *merge_opts)
-{
- git_commit *parent_commit = NULL;
- git_tree *parent_tree = NULL, *our_tree = NULL, *cherrypick_tree = NULL;
- int parent = 0, error = 0;
-
- assert(out && repo && cherrypick_commit && our_commit);
-
- if (git_commit_parentcount(cherrypick_commit) > 1) {
- if (!mainline)
- return cherrypick_seterr(cherrypick_commit,
- "Mainline branch is not specified but %s is a merge commit");
-
- parent = mainline;
- } else {
- if (mainline)
- return cherrypick_seterr(cherrypick_commit,
- "Mainline branch specified but %s is not a merge commit");
-
- parent = git_commit_parentcount(cherrypick_commit);
- }
-
- if (parent &&
- ((error = git_commit_parent(&parent_commit, cherrypick_commit, (parent - 1))) < 0 ||
- (error = git_commit_tree(&parent_tree, parent_commit)) < 0))
- goto done;
-
- if ((error = git_commit_tree(&cherrypick_tree, cherrypick_commit)) < 0 ||
- (error = git_commit_tree(&our_tree, our_commit)) < 0)
- goto done;
-
- error = git_merge_trees(out, repo, parent_tree, our_tree, cherrypick_tree, merge_opts);
-
-done:
- git_tree_free(parent_tree);
- git_tree_free(our_tree);
- git_tree_free(cherrypick_tree);
- git_commit_free(parent_commit);
-
- return error;
-}
-
-int git_cherrypick(
- git_repository *repo,
- git_commit *commit,
- const git_cherrypick_options *given_opts)
-{
- git_cherrypick_options opts;
- git_reference *our_ref = NULL;
- git_commit *our_commit = NULL;
- char commit_oidstr[GIT_OID_HEXSZ + 1];
- const char *commit_msg, *commit_summary;
- git_buf their_label = GIT_BUF_INIT;
- git_index *index = NULL;
- git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
- int error = 0;
-
- assert(repo && commit);
-
- GITERR_CHECK_VERSION(given_opts, GIT_CHERRYPICK_OPTIONS_VERSION, "git_cherrypick_options");
-
- if ((error = git_repository__ensure_not_bare(repo, "cherry-pick")) < 0)
- return error;
-
- if ((commit_msg = git_commit_message(commit)) == NULL ||
- (commit_summary = git_commit_summary(commit)) == NULL) {
- error = -1;
- goto on_error;
- }
-
- git_oid_nfmt(commit_oidstr, sizeof(commit_oidstr), git_commit_id(commit));
-
- if ((error = write_merge_msg(repo, commit_msg)) < 0 ||
- (error = git_buf_printf(&their_label, "%.7s... %s", commit_oidstr, commit_summary)) < 0 ||
- (error = cherrypick_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 ||
- (error = git_indexwriter_init_for_operation(&indexwriter, repo, &opts.checkout_opts.checkout_strategy)) < 0 ||
- (error = write_cherrypick_head(repo, commit_oidstr)) < 0 ||
- (error = git_repository_head(&our_ref, repo)) < 0 ||
- (error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 ||
- (error = git_cherrypick_commit(&index, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
- (error = git_merge__check_result(repo, index)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0 ||
- (error = git_checkout_index(repo, index, &opts.checkout_opts)) < 0 ||
- (error = git_indexwriter_commit(&indexwriter)) < 0)
- goto on_error;
-
- goto done;
-
-on_error:
- cherrypick_state_cleanup(repo);
-
-done:
- git_indexwriter_cleanup(&indexwriter);
- git_index_free(index);
- git_commit_free(our_commit);
- git_reference_free(our_ref);
- git_buf_free(&their_label);
-
- return error;
-}
-
-int git_cherrypick_init_options(
- git_cherrypick_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_cherrypick_options, GIT_CHERRYPICK_OPTIONS_INIT);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include <assert.h>
-
-#include "git2/clone.h"
-#include "git2/remote.h"
-#include "git2/revparse.h"
-#include "git2/branch.h"
-#include "git2/config.h"
-#include "git2/checkout.h"
-#include "git2/commit.h"
-#include "git2/tree.h"
-
-#include "common.h"
-#include "remote.h"
-#include "fileops.h"
-#include "refs.h"
-#include "path.h"
-#include "repository.h"
-#include "odb.h"
-
-static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link);
-
-static int create_branch(
- git_reference **branch,
- git_repository *repo,
- const git_oid *target,
- const char *name,
- const char *log_message)
-{
- git_commit *head_obj = NULL;
- git_reference *branch_ref = NULL;
- git_buf refname = GIT_BUF_INIT;
- int error;
-
- /* Find the target commit */
- if ((error = git_commit_lookup(&head_obj, repo, target)) < 0)
- return error;
-
- /* Create the new branch */
- if ((error = git_buf_printf(&refname, GIT_REFS_HEADS_DIR "%s", name)) < 0)
- return error;
-
- error = git_reference_create(&branch_ref, repo, git_buf_cstr(&refname), target, 0, log_message);
- git_buf_free(&refname);
- git_commit_free(head_obj);
-
- if (!error)
- *branch = branch_ref;
- else
- git_reference_free(branch_ref);
-
- return error;
-}
-
-static int setup_tracking_config(
- git_repository *repo,
- const char *branch_name,
- const char *remote_name,
- const char *merge_target)
-{
- git_config *cfg;
- git_buf remote_key = GIT_BUF_INIT, merge_key = GIT_BUF_INIT;
- int error = -1;
-
- if (git_repository_config__weakptr(&cfg, repo) < 0)
- return -1;
-
- if (git_buf_printf(&remote_key, "branch.%s.remote", branch_name) < 0)
- goto cleanup;
-
- if (git_buf_printf(&merge_key, "branch.%s.merge", branch_name) < 0)
- goto cleanup;
-
- if (git_config_set_string(cfg, git_buf_cstr(&remote_key), remote_name) < 0)
- goto cleanup;
-
- if (git_config_set_string(cfg, git_buf_cstr(&merge_key), merge_target) < 0)
- goto cleanup;
-
- error = 0;
-
-cleanup:
- git_buf_free(&remote_key);
- git_buf_free(&merge_key);
- return error;
-}
-
-static int create_tracking_branch(
- git_reference **branch,
- git_repository *repo,
- const git_oid *target,
- const char *branch_name,
- const char *log_message)
-{
- int error;
-
- if ((error = create_branch(branch, repo, target, branch_name, log_message)) < 0)
- return error;
-
- return setup_tracking_config(
- repo,
- branch_name,
- GIT_REMOTE_ORIGIN,
- git_reference_name(*branch));
-}
-
-static int update_head_to_new_branch(
- git_repository *repo,
- const git_oid *target,
- const char *name,
- const char *reflog_message)
-{
- git_reference *tracking_branch = NULL;
- int error;
-
- if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
- name += strlen(GIT_REFS_HEADS_DIR);
-
- error = create_tracking_branch(&tracking_branch, repo, target, name,
- reflog_message);
-
- if (!error)
- error = git_repository_set_head(
- repo, git_reference_name(tracking_branch));
-
- git_reference_free(tracking_branch);
-
- /* if it already existed, then the user's refspec created it for us, ignore it' */
- if (error == GIT_EEXISTS)
- error = 0;
-
- return error;
-}
-
-static int update_head_to_remote(
- git_repository *repo,
- git_remote *remote,
- const char *reflog_message)
-{
- int error = 0;
- size_t refs_len;
- git_refspec *refspec;
- const git_remote_head *remote_head, **refs;
- const git_oid *remote_head_id;
- git_buf remote_master_name = GIT_BUF_INIT;
- git_buf branch = GIT_BUF_INIT;
-
- if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
- return error;
-
- /* We cloned an empty repository or one with an unborn HEAD */
- if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE))
- return setup_tracking_config(
- repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
-
- /* We know we have HEAD, let's see where it points */
- remote_head = refs[0];
- assert(remote_head);
-
- remote_head_id = &remote_head->oid;
-
- error = git_remote_default_branch(&branch, remote);
- if (error == GIT_ENOTFOUND) {
- error = git_repository_set_head_detached(
- repo, remote_head_id);
- goto cleanup;
- }
-
- refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch));
-
- if (refspec == NULL) {
- giterr_set(GITERR_NET, "the remote's default branch does not fit the refspec configuration");
- error = GIT_EINVALIDSPEC;
- goto cleanup;
- }
-
- /* Determine the remote tracking reference name from the local master */
- if ((error = git_refspec_transform(
- &remote_master_name,
- refspec,
- git_buf_cstr(&branch))) < 0)
- goto cleanup;
-
- error = update_head_to_new_branch(
- repo,
- remote_head_id,
- git_buf_cstr(&branch),
- reflog_message);
-
-cleanup:
- git_buf_free(&remote_master_name);
- git_buf_free(&branch);
-
- return error;
-}
-
-static int update_head_to_branch(
- git_repository *repo,
- const char *remote_name,
- const char *branch,
- const char *reflog_message)
-{
- int retcode;
- git_buf remote_branch_name = GIT_BUF_INIT;
- git_reference* remote_ref = NULL;
-
- assert(remote_name && branch);
-
- if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
- remote_name, branch)) < 0 )
- goto cleanup;
-
- if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
- goto cleanup;
-
- retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch,
- reflog_message);
-
-cleanup:
- git_reference_free(remote_ref);
- git_buf_free(&remote_branch_name);
- return retcode;
-}
-
-static int default_repository_create(git_repository **out, const char *path, int bare, void *payload)
-{
- GIT_UNUSED(payload);
-
- return git_repository_init(out, path, bare);
-}
-
-static int default_remote_create(
- git_remote **out,
- git_repository *repo,
- const char *name,
- const char *url,
- void *payload)
-{
- GIT_UNUSED(payload);
-
- return git_remote_create(out, repo, name, url);
-}
-
-/*
- * submodules?
- */
-
-static int create_and_configure_origin(
- git_remote **out,
- git_repository *repo,
- const char *url,
- const git_clone_options *options)
-{
- int error;
- git_remote *origin = NULL;
- char buf[GIT_PATH_MAX];
- git_remote_create_cb remote_create = options->remote_cb;
- void *payload = options->remote_cb_payload;
-
- /* If the path exists and is a dir, the url should be the absolute path */
- if (git_path_root(url) < 0 && git_path_exists(url) && git_path_isdir(url)) {
- if (p_realpath(url, buf) == NULL)
- return -1;
-
- url = buf;
- }
-
- if (!remote_create) {
- remote_create = default_remote_create;
- payload = NULL;
- }
-
- if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0)
- goto on_error;
-
- *out = origin;
- return 0;
-
-on_error:
- git_remote_free(origin);
- return error;
-}
-
-static bool should_checkout(
- git_repository *repo,
- bool is_bare,
- const git_checkout_options *opts)
-{
- if (is_bare)
- return false;
-
- if (!opts)
- return false;
-
- if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
- return false;
-
- return !git_repository_head_unborn(repo);
-}
-
-static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const char *reflog_message)
-{
- int error;
-
- if (branch)
- error = update_head_to_branch(repo, git_remote_name(remote), branch,
- reflog_message);
- /* Point HEAD to the same ref as the remote's head */
- else
- error = update_head_to_remote(repo, remote, reflog_message);
-
- if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
- error = git_checkout_head(repo, co_opts);
-
- return error;
-}
-
-static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch)
-{
- int error;
- git_buf reflog_message = GIT_BUF_INIT;
- git_fetch_options fetch_opts;
- git_remote *remote;
-
- assert(repo && _remote);
-
- if (!git_repository_is_empty(repo)) {
- giterr_set(GITERR_INVALID, "the repository is not empty");
- return -1;
- }
-
- if ((error = git_remote_dup(&remote, _remote)) < 0)
- return error;
-
- memcpy(&fetch_opts, opts, sizeof(git_fetch_options));
- fetch_opts.update_fetchhead = 0;
- fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
- git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
-
- if ((error = git_remote_fetch(remote, NULL, &fetch_opts, git_buf_cstr(&reflog_message))) != 0)
- goto cleanup;
-
- error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message));
-
-cleanup:
- git_remote_free(remote);
- git_buf_free(&reflog_message);
-
- return error;
-}
-
-int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t local)
-{
- git_buf fromurl = GIT_BUF_INIT;
- const char *path = url_or_path;
- bool is_url, is_local;
-
- if (local == GIT_CLONE_NO_LOCAL)
- return 0;
-
- if ((is_url = git_path_is_local_file_url(url_or_path)) != 0) {
- if (git_path_fromurl(&fromurl, url_or_path) < 0) {
- is_local = -1;
- goto done;
- }
-
- path = fromurl.ptr;
- }
-
- is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) &&
- git_path_isdir(path);
-
-done:
- git_buf_free(&fromurl);
- return is_local;
-}
-
-int git_clone(
- git_repository **out,
- const char *url,
- const char *local_path,
- const git_clone_options *_options)
-{
- int error = 0;
- git_repository *repo = NULL;
- git_remote *origin;
- git_clone_options options = GIT_CLONE_OPTIONS_INIT;
- uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
- git_repository_create_cb repository_cb;
-
- assert(out && url && local_path);
-
- if (_options)
- memcpy(&options, _options, sizeof(git_clone_options));
-
- GITERR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
-
- /* Only clone to a new directory or an empty directory */
- if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
- giterr_set(GITERR_INVALID,
- "'%s' exists and is not an empty directory", local_path);
- return GIT_EEXISTS;
- }
-
- /* Only remove the root directory on failure if we create it */
- if (git_path_exists(local_path))
- rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
-
- if (options.repository_cb)
- repository_cb = options.repository_cb;
- else
- repository_cb = default_repository_create;
-
- if ((error = repository_cb(&repo, local_path, options.bare, options.repository_cb_payload)) < 0)
- return error;
-
- if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
- int clone_local = git_clone__should_clone_local(url, options.local);
- int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;
-
- if (clone_local == 1)
- error = clone_local_into(
- repo, origin, &options.fetch_opts, &options.checkout_opts,
- options.checkout_branch, link);
- else if (clone_local == 0)
- error = clone_into(
- repo, origin, &options.fetch_opts, &options.checkout_opts,
- options.checkout_branch);
- else
- error = -1;
-
- git_remote_free(origin);
- }
-
- if (error != 0) {
- git_error_state last_error = {0};
- giterr_state_capture(&last_error, error);
-
- git_repository_free(repo);
- repo = NULL;
-
- (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
-
- giterr_state_restore(&last_error);
- }
-
- *out = repo;
- return error;
-}
-
-int git_clone_init_options(git_clone_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT);
- return 0;
-}
-
-static bool can_link(const char *src, const char *dst, int link)
-{
-#ifdef GIT_WIN32
- GIT_UNUSED(src);
- GIT_UNUSED(dst);
- GIT_UNUSED(link);
- return false;
-#else
-
- struct stat st_src, st_dst;
-
- if (!link)
- return false;
-
- if (p_stat(src, &st_src) < 0)
- return false;
-
- if (p_stat(dst, &st_dst) < 0)
- return false;
-
- return st_src.st_dev == st_dst.st_dev;
-#endif
-}
-
-static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link)
-{
- int error, flags;
- git_repository *src;
- git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
- git_buf reflog_message = GIT_BUF_INIT;
-
- assert(repo && remote);
-
- if (!git_repository_is_empty(repo)) {
- giterr_set(GITERR_INVALID, "the repository is not empty");
- return -1;
- }
-
- /*
- * Let's figure out what path we should use for the source
- * repo, if it's not rooted, the path should be relative to
- * the repository's worktree/gitdir.
- */
- if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
- return error;
-
- /* Copy .git/objects/ from the source to the target */
- if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
- git_buf_free(&src_path);
- return error;
- }
-
- git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR);
- git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR);
- if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) {
- error = -1;
- goto cleanup;
- }
-
- flags = 0;
- if (can_link(git_repository_path(src), git_repository_path(repo), link))
- flags |= GIT_CPDIR_LINK_FILES;
-
- error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
- flags, GIT_OBJECT_DIR_MODE);
-
- /*
- * can_link() doesn't catch all variations, so if we hit an
- * error and did want to link, let's try again without trying
- * to link.
- */
- if (error < 0 && link) {
- flags &= ~GIT_CPDIR_LINK_FILES;
- error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
- flags, GIT_OBJECT_DIR_MODE);
- }
-
- if (error < 0)
- goto cleanup;
-
- git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
-
- if ((error = git_remote_fetch(remote, NULL, fetch_opts, git_buf_cstr(&reflog_message))) != 0)
- goto cleanup;
-
- error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message));
-
-cleanup:
- git_buf_free(&reflog_message);
- git_buf_free(&src_path);
- git_buf_free(&src_odb);
- git_buf_free(&dst_odb);
- git_repository_free(src);
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_clone_h__
-#define INCLUDE_clone_h__
-
-extern int git_clone__should_clone_local(const char *url, git_clone_local_t local);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/common.h"
-#include "git2/object.h"
-#include "git2/repository.h"
-#include "git2/signature.h"
-#include "git2/sys/commit.h"
-
-#include "common.h"
-#include "odb.h"
-#include "commit.h"
-#include "signature.h"
-#include "message.h"
-#include "refs.h"
-#include "object.h"
-#include "oidarray.h"
-
-void git_commit__free(void *_commit)
-{
- git_commit *commit = _commit;
-
- git_array_clear(commit->parent_ids);
-
- git_signature_free(commit->author);
- git_signature_free(commit->committer);
-
- git__free(commit->raw_header);
- git__free(commit->raw_message);
- git__free(commit->message_encoding);
- git__free(commit->summary);
- git__free(commit->body);
-
- git__free(commit);
-}
-
-static int git_commit__create_buffer_internal(
- git_buf *out,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_oid *tree,
- git_array_oid_t *parents)
-{
- size_t i = 0;
- const git_oid *parent;
-
- assert(out && tree);
-
- git_oid__writebuf(out, "tree ", tree);
-
- for (i = 0; i < git_array_size(*parents); i++) {
- parent = git_array_get(*parents, i);
- git_oid__writebuf(out, "parent ", parent);
- }
-
- git_signature__writebuf(out, "author ", author);
- git_signature__writebuf(out, "committer ", committer);
-
- if (message_encoding != NULL)
- git_buf_printf(out, "encoding %s\n", message_encoding);
-
- git_buf_putc(out, '\n');
-
- if (git_buf_puts(out, message) < 0)
- goto on_error;
-
- return 0;
-
-on_error:
- git_buf_free(out);
- return -1;
-}
-
-static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree,
- git_commit_parent_callback parent_cb, void *parent_payload,
- const git_oid *current_id, bool validate)
-{
- size_t i;
- int error;
- git_oid *parent_cpy;
- const git_oid *parent;
-
- if (validate && !git_object__is_valid(repo, tree, GIT_OBJ_TREE))
- return -1;
-
- i = 0;
- while ((parent = parent_cb(i, parent_payload)) != NULL) {
- if (validate && !git_object__is_valid(repo, parent, GIT_OBJ_COMMIT)) {
- error = -1;
- goto on_error;
- }
-
- parent_cpy = git_array_alloc(*parents);
- GITERR_CHECK_ALLOC(parent_cpy);
-
- git_oid_cpy(parent_cpy, parent);
- i++;
- }
-
- if (current_id && (parents->size == 0 || git_oid_cmp(current_id, git_array_get(*parents, 0)))) {
- giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent");
- error = GIT_EMODIFIED;
- goto on_error;
- }
-
- return 0;
-
-on_error:
- git_array_clear(*parents);
- return error;
-}
-
-static int git_commit__create_internal(
- git_oid *id,
- git_repository *repo,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_oid *tree,
- git_commit_parent_callback parent_cb,
- void *parent_payload,
- bool validate)
-{
- int error;
- git_odb *odb;
- git_reference *ref = NULL;
- git_buf buf = GIT_BUF_INIT;
- const git_oid *current_id = NULL;
- git_array_oid_t parents = GIT_ARRAY_INIT;
-
- if (update_ref) {
- error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
- }
- giterr_clear();
-
- if (ref)
- current_id = git_reference_target(ref);
-
- if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
- goto cleanup;
-
- error = git_commit__create_buffer_internal(&buf, author, committer,
- message_encoding, message, tree,
- &parents);
-
- if (error < 0)
- goto cleanup;
-
- if (git_repository_odb__weakptr(&odb, repo) < 0)
- goto cleanup;
-
- if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJ_COMMIT) < 0)
- goto cleanup;
-
-
- if (update_ref != NULL) {
- error = git_reference__update_for_commit(
- repo, ref, update_ref, id, "commit");
- goto cleanup;
- }
-
-cleanup:
- git_array_clear(parents);
- git_reference_free(ref);
- git_buf_free(&buf);
- return error;
-}
-
-int git_commit_create_from_callback(
- git_oid *id,
- git_repository *repo,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_oid *tree,
- git_commit_parent_callback parent_cb,
- void *parent_payload)
-{
- return git_commit__create_internal(
- id, repo, update_ref, author, committer, message_encoding, message,
- tree, parent_cb, parent_payload, true);
-}
-
-typedef struct {
- size_t total;
- va_list args;
-} commit_parent_varargs;
-
-static const git_oid *commit_parent_from_varargs(size_t curr, void *payload)
-{
- commit_parent_varargs *data = payload;
- const git_commit *commit;
- if (curr >= data->total)
- return NULL;
- commit = va_arg(data->args, const git_commit *);
- return commit ? git_commit_id(commit) : NULL;
-}
-
-int git_commit_create_v(
- git_oid *id,
- git_repository *repo,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_tree *tree,
- size_t parent_count,
- ...)
-{
- int error = 0;
- commit_parent_varargs data;
-
- assert(tree && git_tree_owner(tree) == repo);
-
- data.total = parent_count;
- va_start(data.args, parent_count);
-
- error = git_commit__create_internal(
- id, repo, update_ref, author, committer,
- message_encoding, message, git_tree_id(tree),
- commit_parent_from_varargs, &data, false);
-
- va_end(data.args);
- return error;
-}
-
-typedef struct {
- size_t total;
- const git_oid **parents;
-} commit_parent_oids;
-
-static const git_oid *commit_parent_from_ids(size_t curr, void *payload)
-{
- commit_parent_oids *data = payload;
- return (curr < data->total) ? data->parents[curr] : NULL;
-}
-
-int git_commit_create_from_ids(
- git_oid *id,
- git_repository *repo,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_oid *tree,
- size_t parent_count,
- const git_oid *parents[])
-{
- commit_parent_oids data = { parent_count, parents };
-
- return git_commit__create_internal(
- id, repo, update_ref, author, committer,
- message_encoding, message, tree,
- commit_parent_from_ids, &data, true);
-}
-
-typedef struct {
- size_t total;
- const git_commit **parents;
- git_repository *repo;
-} commit_parent_data;
-
-static const git_oid *commit_parent_from_array(size_t curr, void *payload)
-{
- commit_parent_data *data = payload;
- const git_commit *commit;
- if (curr >= data->total)
- return NULL;
- commit = data->parents[curr];
- if (git_commit_owner(commit) != data->repo)
- return NULL;
- return git_commit_id(commit);
-}
-
-int git_commit_create(
- git_oid *id,
- git_repository *repo,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_tree *tree,
- size_t parent_count,
- const git_commit *parents[])
-{
- commit_parent_data data = { parent_count, parents, repo };
-
- assert(tree && git_tree_owner(tree) == repo);
-
- return git_commit__create_internal(
- id, repo, update_ref, author, committer,
- message_encoding, message, git_tree_id(tree),
- commit_parent_from_array, &data, false);
-}
-
-static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
-{
- const git_commit *commit_to_amend = payload;
- if (curr >= git_array_size(commit_to_amend->parent_ids))
- return NULL;
- return git_array_get(commit_to_amend->parent_ids, curr);
-}
-
-int git_commit_amend(
- git_oid *id,
- const git_commit *commit_to_amend,
- const char *update_ref,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_tree *tree)
-{
- git_repository *repo;
- git_oid tree_id;
- git_reference *ref;
- int error;
-
- assert(id && commit_to_amend);
-
- repo = git_commit_owner(commit_to_amend);
-
- if (!author)
- author = git_commit_author(commit_to_amend);
- if (!committer)
- committer = git_commit_committer(commit_to_amend);
- if (!message_encoding)
- message_encoding = git_commit_message_encoding(commit_to_amend);
- if (!message)
- message = git_commit_message(commit_to_amend);
-
- if (!tree) {
- git_tree *old_tree;
- GITERR_CHECK_ERROR( git_commit_tree(&old_tree, commit_to_amend) );
- git_oid_cpy(&tree_id, git_tree_id(old_tree));
- git_tree_free(old_tree);
- } else {
- assert(git_tree_owner(tree) == repo);
- git_oid_cpy(&tree_id, git_tree_id(tree));
- }
-
- if (update_ref) {
- if ((error = git_reference_lookup_resolved(&ref, repo, update_ref, 5)) < 0)
- return error;
-
- if (git_oid_cmp(git_commit_id(commit_to_amend), git_reference_target(ref))) {
- git_reference_free(ref);
- giterr_set(GITERR_REFERENCE, "commit to amend is not the tip of the given branch");
- return -1;
- }
- }
-
- error = git_commit__create_internal(
- id, repo, NULL, author, committer, message_encoding, message,
- &tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
-
- if (!error && update_ref) {
- error = git_reference__update_for_commit(
- repo, ref, NULL, id, "commit");
- git_reference_free(ref);
- }
-
- return error;
-}
-
-int git_commit__parse(void *_commit, git_odb_object *odb_obj)
-{
- git_commit *commit = _commit;
- const char *buffer_start = git_odb_object_data(odb_obj), *buffer;
- const char *buffer_end = buffer_start + git_odb_object_size(odb_obj);
- git_oid parent_id;
- size_t header_len;
- git_signature dummy_sig;
-
- buffer = buffer_start;
-
- /* Allocate for one, which will allow not to realloc 90% of the time */
- git_array_init_to_size(commit->parent_ids, 1);
- GITERR_CHECK_ARRAY(commit->parent_ids);
-
- /* The tree is always the first field */
- if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
- goto bad_buffer;
-
- /*
- * TODO: commit grafts!
- */
-
- while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
- git_oid *new_id = git_array_alloc(commit->parent_ids);
- GITERR_CHECK_ALLOC(new_id);
-
- git_oid_cpy(new_id, &parent_id);
- }
-
- commit->author = git__malloc(sizeof(git_signature));
- GITERR_CHECK_ALLOC(commit->author);
-
- if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
- return -1;
-
- /* Some tools create multiple author fields, ignore the extra ones */
- while ((size_t)(buffer_end - buffer) >= strlen("author ") && !git__prefixcmp(buffer, "author ")) {
- if (git_signature__parse(&dummy_sig, &buffer, buffer_end, "author ", '\n') < 0)
- return -1;
-
- git__free(dummy_sig.name);
- git__free(dummy_sig.email);
- }
-
- /* Always parse the committer; we need the commit time */
- commit->committer = git__malloc(sizeof(git_signature));
- GITERR_CHECK_ALLOC(commit->committer);
-
- if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
- return -1;
-
- /* Parse add'l header entries */
- while (buffer < buffer_end) {
- const char *eoln = buffer;
- if (buffer[-1] == '\n' && buffer[0] == '\n')
- break;
-
- while (eoln < buffer_end && *eoln != '\n')
- ++eoln;
-
- if (git__prefixcmp(buffer, "encoding ") == 0) {
- buffer += strlen("encoding ");
-
- commit->message_encoding = git__strndup(buffer, eoln - buffer);
- GITERR_CHECK_ALLOC(commit->message_encoding);
- }
-
- if (eoln < buffer_end && *eoln == '\n')
- ++eoln;
- buffer = eoln;
- }
-
- header_len = buffer - buffer_start;
- commit->raw_header = git__strndup(buffer_start, header_len);
- GITERR_CHECK_ALLOC(commit->raw_header);
-
- /* point "buffer" to data after header, +1 for the final LF */
- buffer = buffer_start + header_len + 1;
-
- /* extract commit message */
- if (buffer <= buffer_end)
- commit->raw_message = git__strndup(buffer, buffer_end - buffer);
- else
- commit->raw_message = git__strdup("");
- GITERR_CHECK_ALLOC(commit->raw_message);
-
- return 0;
-
-bad_buffer:
- giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");
- return -1;
-}
-
-#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
- _rvalue git_commit_##_name(const git_commit *commit) \
- {\
- assert(commit); \
- return _return; \
- }
-
-GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
-GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
-GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
-GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
-GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
-GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
-GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
-GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
-GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id)
-
-const char *git_commit_message(const git_commit *commit)
-{
- const char *message;
-
- assert(commit);
-
- message = commit->raw_message;
-
- /* trim leading newlines from raw message */
- while (*message && *message == '\n')
- ++message;
-
- return message;
-}
-
-const char *git_commit_summary(git_commit *commit)
-{
- git_buf summary = GIT_BUF_INIT;
- const char *msg, *space;
- bool space_contains_newline = false;
-
- assert(commit);
-
- if (!commit->summary) {
- for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
- char next_character = msg[0];
- /* stop processing at the end of the first paragraph */
- if (next_character == '\n' && (!msg[1] || msg[1] == '\n'))
- break;
- /* record the beginning of contiguous whitespace runs */
- else if (git__isspace(next_character)) {
- if(space == NULL) {
- space = msg;
- space_contains_newline = false;
- }
- space_contains_newline |= next_character == '\n';
- }
- /* the next character is non-space */
- else {
- /* process any recorded whitespace */
- if (space) {
- if(space_contains_newline)
- git_buf_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */
- else
- git_buf_put(&summary, space, (msg - space)); /* otherwise copy it */
- space = NULL;
- }
- /* copy the next character */
- git_buf_putc(&summary, next_character);
- }
- }
-
- commit->summary = git_buf_detach(&summary);
- if (!commit->summary)
- commit->summary = git__strdup("");
- }
-
- return commit->summary;
-}
-
-const char *git_commit_body(git_commit *commit)
-{
- const char *msg, *end;
-
- assert(commit);
-
- if (!commit->body) {
- /* search for end of summary */
- for (msg = git_commit_message(commit); *msg; ++msg)
- if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
- break;
-
- /* trim leading and trailing whitespace */
- for (; *msg; ++msg)
- if (!git__isspace(*msg))
- break;
- for (end = msg + strlen(msg) - 1; msg <= end; --end)
- if (!git__isspace(*end))
- break;
-
- if (*msg)
- commit->body = git__strndup(msg, end - msg + 1);
- }
-
- return commit->body;
-}
-
-int git_commit_tree(git_tree **tree_out, const git_commit *commit)
-{
- assert(commit);
- return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
-}
-
-const git_oid *git_commit_parent_id(
- const git_commit *commit, unsigned int n)
-{
- assert(commit);
-
- return git_array_get(commit->parent_ids, n);
-}
-
-int git_commit_parent(
- git_commit **parent, const git_commit *commit, unsigned int n)
-{
- const git_oid *parent_id;
- assert(commit);
-
- parent_id = git_commit_parent_id(commit, n);
- if (parent_id == NULL) {
- giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
- return GIT_ENOTFOUND;
- }
-
- return git_commit_lookup(parent, commit->object.repo, parent_id);
-}
-
-int git_commit_nth_gen_ancestor(
- git_commit **ancestor,
- const git_commit *commit,
- unsigned int n)
-{
- git_commit *current, *parent = NULL;
- int error;
-
- assert(ancestor && commit);
-
- if (git_commit_dup(¤t, (git_commit *)commit) < 0)
- return -1;
-
- if (n == 0) {
- *ancestor = current;
- return 0;
- }
-
- while (n--) {
- error = git_commit_parent(&parent, current, 0);
-
- git_commit_free(current);
-
- if (error < 0)
- return error;
-
- current = parent;
- }
-
- *ancestor = parent;
- return 0;
-}
-
-int git_commit_header_field(git_buf *out, const git_commit *commit, const char *field)
-{
- const char *eol, *buf = commit->raw_header;
-
- git_buf_sanitize(out);
-
- while ((eol = strchr(buf, '\n'))) {
- /* We can skip continuations here */
- if (buf[0] == ' ') {
- buf = eol + 1;
- continue;
- }
-
- /* Skip until we find the field we're after */
- if (git__prefixcmp(buf, field)) {
- buf = eol + 1;
- continue;
- }
-
- buf += strlen(field);
- /* Check that we're not matching a prefix but the field itself */
- if (buf[0] != ' ') {
- buf = eol + 1;
- continue;
- }
-
- buf++; /* skip the SP */
-
- git_buf_put(out, buf, eol - buf);
- if (git_buf_oom(out))
- goto oom;
-
- /* If the next line starts with SP, it's multi-line, we must continue */
- while (eol[1] == ' ') {
- git_buf_putc(out, '\n');
- buf = eol + 2;
- eol = strchr(buf, '\n');
- if (!eol)
- goto malformed;
-
- git_buf_put(out, buf, eol - buf);
- }
-
- if (git_buf_oom(out))
- goto oom;
-
- return 0;
- }
-
- giterr_set(GITERR_OBJECT, "no such field '%s'", field);
- return GIT_ENOTFOUND;
-
-malformed:
- giterr_set(GITERR_OBJECT, "malformed header");
- return -1;
-oom:
- giterr_set_oom();
- return -1;
-}
-
-int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field)
-{
- git_odb_object *obj;
- git_odb *odb;
- const char *buf;
- const char *h, *eol;
- int error;
-
- git_buf_sanitize(signature);
- git_buf_sanitize(signed_data);
-
- if (!field)
- field = "gpgsig";
-
- if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
- return error;
-
- if ((error = git_odb_read(&obj, odb, commit_id)) < 0)
- return error;
-
- if (obj->cached.type != GIT_OBJ_COMMIT) {
- giterr_set(GITERR_INVALID, "the requested type does not match the type in ODB");
- error = GIT_ENOTFOUND;
- goto cleanup;
- }
-
- buf = git_odb_object_data(obj);
-
- while ((h = strchr(buf, '\n')) && h[1] != '\0') {
- h++;
- if (git__prefixcmp(buf, field)) {
- if (git_buf_put(signed_data, buf, h - buf) < 0)
- return -1;
-
- buf = h;
- continue;
- }
-
- h = buf;
- h += strlen(field);
- eol = strchr(h, '\n');
- if (h[0] != ' ') {
- buf = h;
- continue;
- }
- if (!eol)
- goto malformed;
-
- h++; /* skip the SP */
-
- git_buf_put(signature, h, eol - h);
- if (git_buf_oom(signature))
- goto oom;
-
- /* If the next line starts with SP, it's multi-line, we must continue */
- while (eol[1] == ' ') {
- git_buf_putc(signature, '\n');
- h = eol + 2;
- eol = strchr(h, '\n');
- if (!eol)
- goto malformed;
-
- git_buf_put(signature, h, eol - h);
- }
-
- if (git_buf_oom(signature))
- goto oom;
-
- git_odb_object_free(obj);
- return git_buf_puts(signed_data, eol+1);
- }
-
- giterr_set(GITERR_OBJECT, "this commit is not signed");
- error = GIT_ENOTFOUND;
- goto cleanup;
-
-malformed:
- giterr_set(GITERR_OBJECT, "malformed header");
- error = -1;
- goto cleanup;
-oom:
- giterr_set_oom();
- error = -1;
- goto cleanup;
-
-cleanup:
- git_odb_object_free(obj);
- git_buf_clear(signature);
- git_buf_clear(signed_data);
- return error;
-}
-
-int git_commit_create_buffer(git_buf *out,
- git_repository *repo,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message,
- const git_tree *tree,
- size_t parent_count,
- const git_commit *parents[])
-{
- int error;
- commit_parent_data data = { parent_count, parents, repo };
- git_array_oid_t parents_arr = GIT_ARRAY_INIT;
- const git_oid *tree_id;
-
- assert(tree && git_tree_owner(tree) == repo);
-
- tree_id = git_tree_id(tree);
-
- if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
- return error;
-
- error = git_commit__create_buffer_internal(
- out, author, committer,
- message_encoding, message, tree_id,
- &parents_arr);
-
- git_array_clear(parents_arr);
- return error;
-}
-
-/**
- * Append to 'out' properly marking continuations when there's a newline in 'content'
- */
-static void format_header_field(git_buf *out, const char *field, const char *content)
-{
- const char *lf;
-
- assert(out && field && content);
-
- git_buf_puts(out, field);
- git_buf_putc(out, ' ');
-
- while ((lf = strchr(content, '\n')) != NULL) {
- git_buf_put(out, content, lf - content);
- git_buf_puts(out, "\n ");
- content = lf + 1;
- }
-
- git_buf_puts(out, content);
- git_buf_putc(out, '\n');
-}
-
-int git_commit_create_with_signature(
- git_oid *out,
- git_repository *repo,
- const char *commit_content,
- const char *signature,
- const char *signature_field)
-{
- git_odb *odb;
- int error = 0;
- const char *field;
- const char *header_end;
- git_buf commit = GIT_BUF_INIT;
-
- /* We start by identifying the end of the commit header */
- header_end = strstr(commit_content, "\n\n");
- if (!header_end) {
- giterr_set(GITERR_INVALID, "malformed commit contents");
- return -1;
- }
-
- field = signature_field ? signature_field : "gpgsig";
-
- /* The header ends after the first LF */
- header_end++;
- git_buf_put(&commit, commit_content, header_end - commit_content);
- format_header_field(&commit, field, signature);
- git_buf_puts(&commit, header_end);
-
- if (git_buf_oom(&commit))
- return -1;
-
- if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
- goto cleanup;
-
- if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT)) < 0)
- goto cleanup;
-
-cleanup:
- git_buf_free(&commit);
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_commit_h__
-#define INCLUDE_commit_h__
-
-#include "git2/commit.h"
-#include "tree.h"
-#include "repository.h"
-#include "array.h"
-
-#include <time.h>
-
-struct git_commit {
- git_object object;
-
- git_array_t(git_oid) parent_ids;
- git_oid tree_id;
-
- git_signature *author;
- git_signature *committer;
-
- char *message_encoding;
- char *raw_message;
- char *raw_header;
-
- char *summary;
- char *body;
-};
-
-void git_commit__free(void *commit);
-int git_commit__parse(void *commit, git_odb_object *obj);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "commit_list.h"
-#include "common.h"
-#include "revwalk.h"
-#include "pool.h"
-#include "odb.h"
-
-int git_commit_list_time_cmp(const void *a, const void *b)
-{
- int64_t time_a = ((git_commit_list_node *) a)->time;
- int64_t time_b = ((git_commit_list_node *) b)->time;
-
- if (time_a < time_b)
- return 1;
- if (time_a > time_b)
- return -1;
-
- return 0;
-}
-
-git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p)
-{
- git_commit_list *new_list = git__malloc(sizeof(git_commit_list));
- if (new_list != NULL) {
- new_list->item = item;
- new_list->next = *list_p;
- }
- *list_p = new_list;
- return new_list;
-}
-
-git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p)
-{
- git_commit_list **pp = list_p;
- git_commit_list *p;
-
- while ((p = *pp) != NULL) {
- if (git_commit_list_time_cmp(p->item, item) > 0)
- break;
-
- pp = &p->next;
- }
-
- return git_commit_list_insert(item, pp);
-}
-
-git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk)
-{
- return (git_commit_list_node *)git_pool_mallocz(&walk->commit_pool, 1);
-}
-
-static int commit_error(git_commit_list_node *commit, const char *msg)
-{
- char commit_oid[GIT_OID_HEXSZ + 1];
- git_oid_fmt(commit_oid, &commit->oid);
- commit_oid[GIT_OID_HEXSZ] = '\0';
-
- giterr_set(GITERR_ODB, "Failed to parse commit %s - %s", commit_oid, msg);
-
- return -1;
-}
-
-static git_commit_list_node **alloc_parents(
- git_revwalk *walk, git_commit_list_node *commit, size_t n_parents)
-{
- if (n_parents <= PARENTS_PER_COMMIT)
- return (git_commit_list_node **)((char *)commit + sizeof(git_commit_list_node));
-
- return (git_commit_list_node **)git_pool_malloc(
- &walk->commit_pool, (uint32_t)(n_parents * sizeof(git_commit_list_node *)));
-}
-
-
-void git_commit_list_free(git_commit_list **list_p)
-{
- git_commit_list *list = *list_p;
-
- if (list == NULL)
- return;
-
- while (list) {
- git_commit_list *temp = list;
- list = temp->next;
- git__free(temp);
- }
-
- *list_p = NULL;
-}
-
-git_commit_list_node *git_commit_list_pop(git_commit_list **stack)
-{
- git_commit_list *top = *stack;
- git_commit_list_node *item = top ? top->item : NULL;
-
- if (top) {
- *stack = top->next;
- git__free(top);
- }
- return item;
-}
-
-static int commit_quick_parse(
- git_revwalk *walk,
- git_commit_list_node *commit,
- const uint8_t *buffer,
- size_t buffer_len)
-{
- const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
- const uint8_t *buffer_end = buffer + buffer_len;
- const uint8_t *parents_start, *committer_start;
- int i, parents = 0;
- int64_t commit_time;
-
- buffer += strlen("tree ") + GIT_OID_HEXSZ + 1;
-
- parents_start = buffer;
- while (buffer + parent_len < buffer_end && memcmp(buffer, "parent ", strlen("parent ")) == 0) {
- parents++;
- buffer += parent_len;
- }
-
- commit->parents = alloc_parents(walk, commit, parents);
- GITERR_CHECK_ALLOC(commit->parents);
-
- buffer = parents_start;
- for (i = 0; i < parents; ++i) {
- git_oid oid;
-
- if (git_oid_fromstr(&oid, (const char *)buffer + strlen("parent ")) < 0)
- return -1;
-
- commit->parents[i] = git_revwalk__commit_lookup(walk, &oid);
- if (commit->parents[i] == NULL)
- return -1;
-
- buffer += parent_len;
- }
-
- commit->out_degree = (unsigned short)parents;
-
- if ((committer_start = buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
- return commit_error(commit, "object is corrupted");
-
- buffer++;
-
- if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
- return commit_error(commit, "object is corrupted");
-
- /* Skip trailing spaces */
- while (buffer > committer_start && git__isspace(*buffer))
- buffer--;
-
- /* Seek for the beginning of the pack of digits */
- while (buffer > committer_start && git__isdigit(*buffer))
- buffer--;
-
- /* Skip potential timezone offset */
- if ((buffer > committer_start) && (*buffer == '+' || *buffer == '-')) {
- buffer--;
-
- while (buffer > committer_start && git__isspace(*buffer))
- buffer--;
-
- while (buffer > committer_start && git__isdigit(*buffer))
- buffer--;
- }
-
- if ((buffer == committer_start) || (git__strtol64(&commit_time, (char *)(buffer + 1), NULL, 10) < 0))
- return commit_error(commit, "cannot parse commit time");
-
- commit->time = commit_time;
- commit->parsed = 1;
- return 0;
-}
-
-int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit)
-{
- git_odb_object *obj;
- int error;
-
- if (commit->parsed)
- return 0;
-
- if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
- return error;
-
- if (obj->cached.type != GIT_OBJ_COMMIT) {
- giterr_set(GITERR_INVALID, "Object is no commit object");
- error = -1;
- } else
- error = commit_quick_parse(
- walk, commit,
- (const uint8_t *)git_odb_object_data(obj),
- git_odb_object_size(obj));
-
- git_odb_object_free(obj);
- return error;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_commit_list_h__
-#define INCLUDE_commit_list_h__
-
-#include "git2/oid.h"
-
-#define PARENT1 (1 << 0)
-#define PARENT2 (1 << 1)
-#define RESULT (1 << 2)
-#define STALE (1 << 3)
-#define ALL_FLAGS (PARENT1 | PARENT2 | STALE | RESULT)
-
-#define PARENTS_PER_COMMIT 2
-#define COMMIT_ALLOC \
- (sizeof(git_commit_list_node) + PARENTS_PER_COMMIT * sizeof(git_commit_list_node *))
-
-#define FLAG_BITS 4
-
-typedef struct git_commit_list_node {
- git_oid oid;
- int64_t time;
- unsigned int seen:1,
- uninteresting:1,
- topo_delay:1,
- parsed:1,
- added:1,
- flags : FLAG_BITS;
-
- unsigned short in_degree;
- unsigned short out_degree;
-
- struct git_commit_list_node **parents;
-} git_commit_list_node;
-
-typedef struct git_commit_list {
- git_commit_list_node *item;
- struct git_commit_list *next;
-} git_commit_list;
-
-git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk);
-int git_commit_list_time_cmp(const void *a, const void *b);
-void git_commit_list_free(git_commit_list **list_p);
-git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p);
-git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p);
-int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit);
-git_commit_list_node *git_commit_list_pop(git_commit_list **stack);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_common_h__
-#define INCLUDE_common_h__
-
-#include "git2/common.h"
-#include "cc-compat.h"
-
-/** Declare a function as always inlined. */
-#if defined(_MSC_VER)
-# define GIT_INLINE(type) static __inline type
-#else
-# define GIT_INLINE(type) static inline type
-#endif
-
-/** Support for gcc/clang __has_builtin intrinsic */
-#ifndef __has_builtin
-# define __has_builtin(x) 0
-#endif
-
-#include <assert.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifdef GIT_WIN32
-
-# include <io.h>
-# include <direct.h>
-# include <winsock2.h>
-# include <windows.h>
-# include <ws2tcpip.h>
-# include "win32/msvc-compat.h"
-# include "win32/mingw-compat.h"
-# include "win32/win32-compat.h"
-# include "win32/error.h"
-# include "win32/version.h"
-# ifdef GIT_THREADS
-# include "win32/thread.h"
-# endif
-# if defined(GIT_MSVC_CRTDBG)
-# include "win32/w32_stack.h"
-# include "win32/w32_crtdbg_stacktrace.h"
-# endif
-
-#else
-
-# include <unistd.h>
-# include <strings.h>
-# ifdef GIT_THREADS
-# include <pthread.h>
-# include <sched.h>
-# endif
-#define GIT_STDLIB_CALL
-
-#ifdef GIT_USE_STAT_ATIMESPEC
-# define st_atim st_atimespec
-# define st_ctim st_ctimespec
-# define st_mtim st_mtimespec
-#endif
-
-# include <arpa/inet.h>
-
-#endif
-
-#include "git2/types.h"
-#include "git2/errors.h"
-#include "thread-utils.h"
-#include "integer.h"
-
-#include <regex.h>
-
-#define DEFAULT_BUFSIZE 65536
-#define FILEIO_BUFSIZE DEFAULT_BUFSIZE
-#define FILTERIO_BUFSIZE DEFAULT_BUFSIZE
-#define NETIO_BUFSIZE DEFAULT_BUFSIZE
-
-/**
- * Check a pointer allocation result, returning -1 if it failed.
- */
-#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }
-
-/**
- * Check a buffer allocation result, returning -1 if it failed.
- */
-#define GITERR_CHECK_ALLOC_BUF(buf) if ((void *)(buf) == NULL || git_buf_oom(buf)) { return -1; }
-
-/**
- * Check a return value and propagate result if non-zero.
- */
-#define GITERR_CHECK_ERROR(code) \
- do { int _err = (code); if (_err) return _err; } while (0)
-
-/**
- * Set the error message for this thread, formatting as needed.
- */
-
-void giterr_set(int error_class, const char *string, ...) GIT_FORMAT_PRINTF(2, 3);
-
-/**
- * Set the error message for a regex failure, using the internal regex
- * error code lookup and return a libgit error code.
- */
-int giterr_set_regex(const regex_t *regex, int error_code);
-
-/**
- * Set error message for user callback if needed.
- *
- * If the error code in non-zero and no error message is set, this
- * sets a generic error message.
- *
- * @return This always returns the `error_code` parameter.
- */
-GIT_INLINE(int) giterr_set_after_callback_function(
- int error_code, const char *action)
-{
- if (error_code) {
- const git_error *e = giterr_last();
- if (!e || !e->message)
- giterr_set(e ? e->klass : GITERR_CALLBACK,
- "%s callback returned %d", action, error_code);
- }
- return error_code;
-}
-
-#ifdef GIT_WIN32
-#define giterr_set_after_callback(code) \
- giterr_set_after_callback_function((code), __FUNCTION__)
-#else
-#define giterr_set_after_callback(code) \
- giterr_set_after_callback_function((code), __func__)
-#endif
-
-/**
- * Gets the system error code for this thread.
- */
-int giterr_system_last(void);
-
-/**
- * Sets the system error code for this thread.
- */
-void giterr_system_set(int code);
-
-/**
- * Structure to preserve libgit2 error state
- */
-typedef struct {
- int error_code;
- unsigned int oom : 1;
- git_error error_msg;
-} git_error_state;
-
-/**
- * Capture current error state to restore later, returning error code.
- * If `error_code` is zero, this does not clear the current error state.
- * You must either restore this error state, or free it.
- */
-extern int giterr_state_capture(git_error_state *state, int error_code);
-
-/**
- * Restore error state to a previous value, returning saved error code.
- */
-extern int giterr_state_restore(git_error_state *state);
-
-/** Free an error state. */
-extern void giterr_state_free(git_error_state *state);
-
-/**
- * Check a versioned structure for validity
- */
-GIT_INLINE(int) giterr__check_version(const void *structure, unsigned int expected_max, const char *name)
-{
- unsigned int actual;
-
- if (!structure)
- return 0;
-
- actual = *(const unsigned int*)structure;
- if (actual > 0 && actual <= expected_max)
- return 0;
-
- giterr_set(GITERR_INVALID, "Invalid version %d on %s", actual, name);
- return -1;
-}
-#define GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) return -1
-
-/**
- * Initialize a structure with a version.
- */
-GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int version)
-{
- memset(structure, 0, len);
- *((int*)structure) = version;
-}
-#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V)
-
-#define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \
- TYPE _tmpl = TPL; \
- GITERR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \
- memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0)
-
-
-/** Check for additive overflow, setting an error if would occur. */
-#define GIT_ADD_SIZET_OVERFLOW(out, one, two) \
- (git__add_sizet_overflow(out, one, two) ? (giterr_set_oom(), 1) : 0)
-
-/** Check for additive overflow, setting an error if would occur. */
-#define GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize) \
- (git__multiply_sizet_overflow(out, nelem, elsize) ? (giterr_set_oom(), 1) : 0)
-
-/** Check for additive overflow, failing if it would occur. */
-#define GITERR_CHECK_ALLOC_ADD(out, one, two) \
- if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { return -1; }
-
-#define GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \
- if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
- GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { return -1; }
-
-#define GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \
- if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
- GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
- GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { return -1; }
-
-/** Check for multiplicative overflow, failing if it would occur. */
-#define GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \
- if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; }
-
-/* NOTE: other giterr functions are in the public errors.h header file */
-
-#include "util.h"
-
-#endif /* INCLUDE_common_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "sysdir.h"
-#include "config.h"
-#include "git2/config.h"
-#include "git2/sys/config.h"
-#include "vector.h"
-#include "buf_text.h"
-#include "config_file.h"
-#include "transaction.h"
-#if GIT_WIN32
-# include <windows.h>
-#endif
-
-#include <ctype.h>
-
-void git_config_entry_free(git_config_entry *entry)
-{
- if (!entry)
- return;
-
- entry->free(entry);
-}
-
-typedef struct {
- git_refcount rc;
-
- git_config_backend *file;
- git_config_level_t level;
-} file_internal;
-
-static void file_internal_free(file_internal *internal)
-{
- git_config_backend *file;
-
- file = internal->file;
- file->free(file);
- git__free(internal);
-}
-
-static void config_free(git_config *cfg)
-{
- size_t i;
- file_internal *internal;
-
- for (i = 0; i < cfg->files.length; ++i) {
- internal = git_vector_get(&cfg->files, i);
- GIT_REFCOUNT_DEC(internal, file_internal_free);
- }
-
- git_vector_free(&cfg->files);
-
- git__memzero(cfg, sizeof(*cfg));
- git__free(cfg);
-}
-
-void git_config_free(git_config *cfg)
-{
- if (cfg == NULL)
- return;
-
- GIT_REFCOUNT_DEC(cfg, config_free);
-}
-
-static int config_backend_cmp(const void *a, const void *b)
-{
- const file_internal *bk_a = (const file_internal *)(a);
- const file_internal *bk_b = (const file_internal *)(b);
-
- return bk_b->level - bk_a->level;
-}
-
-int git_config_new(git_config **out)
-{
- git_config *cfg;
-
- cfg = git__malloc(sizeof(git_config));
- GITERR_CHECK_ALLOC(cfg);
-
- memset(cfg, 0x0, sizeof(git_config));
-
- if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
- git__free(cfg);
- return -1;
- }
-
- *out = cfg;
- GIT_REFCOUNT_INC(cfg);
- return 0;
-}
-
-int git_config_add_file_ondisk(
- git_config *cfg,
- const char *path,
- git_config_level_t level,
- int force)
-{
- git_config_backend *file = NULL;
- struct stat st;
- int res;
-
- assert(cfg && path);
-
- res = p_stat(path, &st);
- if (res < 0 && errno != ENOENT) {
- giterr_set(GITERR_CONFIG, "Error stat'ing config file '%s'", path);
- return -1;
- }
-
- if (git_config_file__ondisk(&file, path) < 0)
- return -1;
-
- if ((res = git_config_add_backend(cfg, file, level, force)) < 0) {
- /*
- * free manually; the file is not owned by the config
- * instance yet and will not be freed on cleanup
- */
- file->free(file);
- return res;
- }
-
- return 0;
-}
-
-int git_config_open_ondisk(git_config **out, const char *path)
-{
- int error;
- git_config *config;
-
- *out = NULL;
-
- if (git_config_new(&config) < 0)
- return -1;
-
- if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0)
- git_config_free(config);
- else
- *out = config;
-
- return error;
-}
-
-int git_config_snapshot(git_config **out, git_config *in)
-{
- int error = 0;
- size_t i;
- file_internal *internal;
- git_config *config;
-
- *out = NULL;
-
- if (git_config_new(&config) < 0)
- return -1;
-
- git_vector_foreach(&in->files, i, internal) {
- git_config_backend *b;
-
- if ((error = internal->file->snapshot(&b, internal->file)) < 0)
- break;
-
- if ((error = git_config_add_backend(config, b, internal->level, 0)) < 0) {
- b->free(b);
- break;
- }
- }
-
- if (error < 0)
- git_config_free(config);
- else
- *out = config;
-
- return error;
-}
-
-static int find_internal_file_by_level(
- file_internal **internal_out,
- const git_config *cfg,
- git_config_level_t level)
-{
- int pos = -1;
- file_internal *internal;
- size_t i;
-
- /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file
- * which has the highest level. As config files are stored in a vector
- * sorted by decreasing order of level, getting the file at position 0
- * will do the job.
- */
- if (level == GIT_CONFIG_HIGHEST_LEVEL) {
- pos = 0;
- } else {
- git_vector_foreach(&cfg->files, i, internal) {
- if (internal->level == level)
- pos = (int)i;
- }
- }
-
- if (pos == -1) {
- giterr_set(GITERR_CONFIG,
- "No config file exists for the given level '%i'", (int)level);
- return GIT_ENOTFOUND;
- }
-
- *internal_out = git_vector_get(&cfg->files, pos);
-
- return 0;
-}
-
-static int duplicate_level(void **old_raw, void *new_raw)
-{
- file_internal **old = (file_internal **)old_raw;
-
- GIT_UNUSED(new_raw);
-
- giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (int)(*old)->level);
- return GIT_EEXISTS;
-}
-
-static void try_remove_existing_file_internal(
- git_config *cfg,
- git_config_level_t level)
-{
- int pos = -1;
- file_internal *internal;
- size_t i;
-
- git_vector_foreach(&cfg->files, i, internal) {
- if (internal->level == level)
- pos = (int)i;
- }
-
- if (pos == -1)
- return;
-
- internal = git_vector_get(&cfg->files, pos);
-
- if (git_vector_remove(&cfg->files, pos) < 0)
- return;
-
- GIT_REFCOUNT_DEC(internal, file_internal_free);
-}
-
-static int git_config__add_internal(
- git_config *cfg,
- file_internal *internal,
- git_config_level_t level,
- int force)
-{
- int result;
-
- /* delete existing config file for level if it exists */
- if (force)
- try_remove_existing_file_internal(cfg, level);
-
- if ((result = git_vector_insert_sorted(&cfg->files,
- internal, &duplicate_level)) < 0)
- return result;
-
- git_vector_sort(&cfg->files);
- internal->file->cfg = cfg;
-
- GIT_REFCOUNT_INC(internal);
-
- return 0;
-}
-
-int git_config_open_global(git_config **cfg_out, git_config *cfg)
-{
- if (!git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_XDG))
- return 0;
-
- return git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_GLOBAL);
-}
-
-int git_config_open_level(
- git_config **cfg_out,
- const git_config *cfg_parent,
- git_config_level_t level)
-{
- git_config *cfg;
- file_internal *internal;
- int res;
-
- if ((res = find_internal_file_by_level(&internal, cfg_parent, level)) < 0)
- return res;
-
- if ((res = git_config_new(&cfg)) < 0)
- return res;
-
- if ((res = git_config__add_internal(cfg, internal, level, true)) < 0) {
- git_config_free(cfg);
- return res;
- }
-
- *cfg_out = cfg;
-
- return 0;
-}
-
-int git_config_add_backend(
- git_config *cfg,
- git_config_backend *file,
- git_config_level_t level,
- int force)
-{
- file_internal *internal;
- int result;
-
- assert(cfg && file);
-
- GITERR_CHECK_VERSION(file, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
-
- if ((result = file->open(file, level)) < 0)
- return result;
-
- internal = git__malloc(sizeof(file_internal));
- GITERR_CHECK_ALLOC(internal);
-
- memset(internal, 0x0, sizeof(file_internal));
-
- internal->file = file;
- internal->level = level;
-
- if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) {
- git__free(internal);
- return result;
- }
-
- return 0;
-}
-
-/*
- * Loop over all the variables
- */
-
-typedef struct {
- git_config_iterator parent;
- git_config_iterator *current;
- const git_config *cfg;
- regex_t regex;
- size_t i;
-} all_iter;
-
-static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
-{
- file_internal *internal;
-
- for (; i > 0; --i) {
- internal = git_vector_get(&cfg->files, i - 1);
- if (!internal || !internal->file)
- continue;
-
- *out = i;
- return 0;
- }
-
- return -1;
-}
-
-static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
-{
- all_iter *iter = (all_iter *) _iter;
- file_internal *internal;
- git_config_backend *backend;
- size_t i;
- int error = 0;
-
- if (iter->current != NULL &&
- (error = iter->current->next(entry, iter->current)) == 0) {
- return 0;
- }
-
- if (error < 0 && error != GIT_ITEROVER)
- return error;
-
- do {
- if (find_next_backend(&i, iter->cfg, iter->i) < 0)
- return GIT_ITEROVER;
-
- internal = git_vector_get(&iter->cfg->files, i - 1);
- backend = internal->file;
- iter->i = i - 1;
-
- if (iter->current)
- iter->current->free(iter->current);
-
- iter->current = NULL;
- error = backend->iterator(&iter->current, backend);
- if (error == GIT_ENOTFOUND)
- continue;
-
- if (error < 0)
- return error;
-
- error = iter->current->next(entry, iter->current);
- /* If this backend is empty, then keep going */
- if (error == GIT_ITEROVER)
- continue;
-
- return error;
-
- } while(1);
-
- return GIT_ITEROVER;
-}
-
-static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter)
-{
- int error;
- all_iter *iter = (all_iter *) _iter;
-
- /*
- * We use the "normal" function to grab the next one across
- * backends and then apply the regex
- */
- while ((error = all_iter_next(entry, _iter)) == 0) {
- /* skip non-matching keys if regexp was provided */
- if (regexec(&iter->regex, (*entry)->name, 0, NULL, 0) != 0)
- continue;
-
- /* and simply return if we like the entry's name */
- return 0;
- }
-
- return error;
-}
-
-static void all_iter_free(git_config_iterator *_iter)
-{
- all_iter *iter = (all_iter *) _iter;
-
- if (iter->current)
- iter->current->free(iter->current);
-
- git__free(iter);
-}
-
-static void all_iter_glob_free(git_config_iterator *_iter)
-{
- all_iter *iter = (all_iter *) _iter;
-
- regfree(&iter->regex);
- all_iter_free(_iter);
-}
-
-int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
-{
- all_iter *iter;
-
- iter = git__calloc(1, sizeof(all_iter));
- GITERR_CHECK_ALLOC(iter);
-
- iter->parent.free = all_iter_free;
- iter->parent.next = all_iter_next;
-
- iter->i = cfg->files.length;
- iter->cfg = cfg;
-
- *out = (git_config_iterator *) iter;
-
- return 0;
-}
-
-int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
-{
- all_iter *iter;
- int result;
-
- if (regexp == NULL)
- return git_config_iterator_new(out, cfg);
-
- iter = git__calloc(1, sizeof(all_iter));
- GITERR_CHECK_ALLOC(iter);
-
- if ((result = p_regcomp(&iter->regex, regexp, REG_EXTENDED)) != 0) {
- giterr_set_regex(&iter->regex, result);
- git__free(iter);
- return -1;
- }
-
- iter->parent.next = all_iter_glob_next;
- iter->parent.free = all_iter_glob_free;
- iter->i = cfg->files.length;
- iter->cfg = cfg;
-
- *out = (git_config_iterator *) iter;
-
- return 0;
-}
-
-int git_config_foreach(
- const git_config *cfg, git_config_foreach_cb cb, void *payload)
-{
- return git_config_foreach_match(cfg, NULL, cb, payload);
-}
-
-int git_config_backend_foreach_match(
- git_config_backend *backend,
- const char *regexp,
- git_config_foreach_cb cb,
- void *payload)
-{
- git_config_entry *entry;
- git_config_iterator* iter;
- regex_t regex;
- int error = 0;
-
- if (regexp != NULL) {
- if ((error = p_regcomp(®ex, regexp, REG_EXTENDED)) != 0) {
- giterr_set_regex(®ex, error);
- regfree(®ex);
- return -1;
- }
- }
-
- if ((error = backend->iterator(&iter, backend)) < 0) {
- iter = NULL;
- return -1;
- }
-
- while (!(iter->next(&entry, iter) < 0)) {
- /* skip non-matching keys if regexp was provided */
- if (regexp && regexec(®ex, entry->name, 0, NULL, 0) != 0)
- continue;
-
- /* abort iterator on non-zero return value */
- if ((error = cb(entry, payload)) != 0) {
- giterr_set_after_callback(error);
- break;
- }
- }
-
- if (regexp != NULL)
- regfree(®ex);
-
- iter->free(iter);
-
- return error;
-}
-
-int git_config_foreach_match(
- const git_config *cfg,
- const char *regexp,
- git_config_foreach_cb cb,
- void *payload)
-{
- int error;
- git_config_iterator *iter;
- git_config_entry *entry;
-
- if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0)
- return error;
-
- while (!(error = git_config_next(&entry, iter))) {
- if ((error = cb(entry, payload)) != 0) {
- giterr_set_after_callback(error);
- break;
- }
- }
-
- git_config_iterator_free(iter);
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- return error;
-}
-
-/**************
- * Setters
- **************/
-
-static int config_error_nofiles(const char *name)
-{
- giterr_set(GITERR_CONFIG,
- "Cannot set value for '%s' when no config files exist", name);
- return GIT_ENOTFOUND;
-}
-
-int git_config_delete_entry(git_config *cfg, const char *name)
-{
- git_config_backend *file;
- file_internal *internal;
-
- internal = git_vector_get(&cfg->files, 0);
- if (!internal || !internal->file)
- return config_error_nofiles(name);
- file = internal->file;
-
- return file->del(file, name);
-}
-
-int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
-{
- char str_value[32]; /* All numbers should fit in here */
- p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);
- return git_config_set_string(cfg, name, str_value);
-}
-
-int git_config_set_int32(git_config *cfg, const char *name, int32_t value)
-{
- return git_config_set_int64(cfg, name, (int64_t)value);
-}
-
-int git_config_set_bool(git_config *cfg, const char *name, int value)
-{
- return git_config_set_string(cfg, name, value ? "true" : "false");
-}
-
-int git_config_set_string(git_config *cfg, const char *name, const char *value)
-{
- int error;
- git_config_backend *file;
- file_internal *internal;
-
- if (!value) {
- giterr_set(GITERR_CONFIG, "The value to set cannot be NULL");
- return -1;
- }
-
- internal = git_vector_get(&cfg->files, 0);
- if (!internal || !internal->file)
- return config_error_nofiles(name);
- file = internal->file;
-
- error = file->set(file, name, value);
-
- if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
- git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg));
-
- return error;
-}
-
-int git_config__update_entry(
- git_config *config,
- const char *key,
- const char *value,
- bool overwrite_existing,
- bool only_if_existing)
-{
- int error = 0;
- git_config_entry *ce = NULL;
-
- if ((error = git_config__lookup_entry(&ce, config, key, false)) < 0)
- return error;
-
- if (!ce && only_if_existing) /* entry doesn't exist */
- return 0;
- if (ce && !overwrite_existing) /* entry would be overwritten */
- return 0;
- if (value && ce && ce->value && !strcmp(ce->value, value)) /* no change */
- return 0;
- if (!value && (!ce || !ce->value)) /* asked to delete absent entry */
- return 0;
-
- if (!value)
- error = git_config_delete_entry(config, key);
- else
- error = git_config_set_string(config, key, value);
-
- git_config_entry_free(ce);
- return error;
-}
-
-/***********
- * Getters
- ***********/
-
-static int config_error_notfound(const char *name)
-{
- giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name);
- return GIT_ENOTFOUND;
-}
-
-enum {
- GET_ALL_ERRORS = 0,
- GET_NO_MISSING = 1,
- GET_NO_ERRORS = 2
-};
-
-static int get_entry(
- git_config_entry **out,
- const git_config *cfg,
- const char *name,
- bool normalize_name,
- int want_errors)
-{
- int res = GIT_ENOTFOUND;
- const char *key = name;
- char *normalized = NULL;
- size_t i;
- file_internal *internal;
-
- *out = NULL;
-
- if (normalize_name) {
- if ((res = git_config__normalize_name(name, &normalized)) < 0)
- goto cleanup;
- key = normalized;
- }
-
- res = GIT_ENOTFOUND;
- git_vector_foreach(&cfg->files, i, internal) {
- if (!internal || !internal->file)
- continue;
-
- res = internal->file->get(internal->file, key, out);
- if (res != GIT_ENOTFOUND)
- break;
- }
-
- git__free(normalized);
-
-cleanup:
- if (res == GIT_ENOTFOUND)
- res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name);
- else if (res && (want_errors == GET_NO_ERRORS)) {
- giterr_clear();
- res = 0;
- }
-
- return res;
-}
-
-int git_config_get_entry(
- git_config_entry **out, const git_config *cfg, const char *name)
-{
- return get_entry(out, cfg, name, true, GET_ALL_ERRORS);
-}
-
-int git_config__lookup_entry(
- git_config_entry **out,
- const git_config *cfg,
- const char *key,
- bool no_errors)
-{
- return get_entry(
- out, cfg, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING);
-}
-
-int git_config_get_mapped(
- int *out,
- const git_config *cfg,
- const char *name,
- const git_cvar_map *maps,
- size_t map_n)
-{
- git_config_entry *entry;
- int ret;
-
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
- return ret;
-
- ret = git_config_lookup_map_value(out, maps, map_n, entry->value);
- git_config_entry_free(entry);
-
- return ret;
-}
-
-int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name)
-{
- git_config_entry *entry;
- int ret;
-
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
- return ret;
-
- ret = git_config_parse_int64(out, entry->value);
- git_config_entry_free(entry);
-
- return ret;
-}
-
-int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name)
-{
- git_config_entry *entry;
- int ret;
-
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
- return ret;
-
- ret = git_config_parse_int32(out, entry->value);
- git_config_entry_free(entry);
-
- return ret;
-}
-
-int git_config_get_bool(int *out, const git_config *cfg, const char *name)
-{
- git_config_entry *entry;
- int ret;
-
- if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
- return ret;
-
- ret = git_config_parse_bool(out, entry->value);
- git_config_entry_free(entry);
-
- return ret;
-}
-
-static int is_readonly(const git_config *cfg)
-{
- size_t i;
- file_internal *internal;
-
- git_vector_foreach(&cfg->files, i, internal) {
- if (!internal || !internal->file)
- continue;
-
- if (!internal->file->readonly)
- return 0;
- }
-
- return 1;
-}
-
-int git_config_get_path(git_buf *out, const git_config *cfg, const char *name)
-{
- git_config_entry *entry;
- int error;
-
- if ((error = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
- return error;
-
- error = git_config_parse_path(out, entry->value);
- git_config_entry_free(entry);
-
- return error;
-}
-
-int git_config_get_string(
- const char **out, const git_config *cfg, const char *name)
-{
- git_config_entry *entry;
- int ret;
-
- if (!is_readonly(cfg)) {
- giterr_set(GITERR_CONFIG, "get_string called on a live config object");
- return -1;
- }
-
- ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
- *out = !ret ? (entry->value ? entry->value : "") : NULL;
-
- git_config_entry_free(entry);
-
- return ret;
-}
-
-int git_config_get_string_buf(
- git_buf *out, const git_config *cfg, const char *name)
-{
- git_config_entry *entry;
- int ret;
- const char *str;
-
- git_buf_sanitize(out);
-
- ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
- str = !ret ? (entry->value ? entry->value : "") : NULL;
-
- if (str)
- ret = git_buf_puts(out, str);
-
- git_config_entry_free(entry);
-
- return ret;
-}
-
-char *git_config__get_string_force(
- const git_config *cfg, const char *key, const char *fallback_value)
-{
- git_config_entry *entry;
- char *ret;
-
- get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
- ret = (entry && entry->value) ? git__strdup(entry->value) : fallback_value ? git__strdup(fallback_value) : NULL;
- git_config_entry_free(entry);
-
- return ret;
-}
-
-int git_config__get_bool_force(
- const git_config *cfg, const char *key, int fallback_value)
-{
- int val = fallback_value;
- git_config_entry *entry;
-
- get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
-
- if (entry && git_config_parse_bool(&val, entry->value) < 0)
- giterr_clear();
-
- git_config_entry_free(entry);
- return val;
-}
-
-int git_config__get_int_force(
- const git_config *cfg, const char *key, int fallback_value)
-{
- int32_t val = (int32_t)fallback_value;
- git_config_entry *entry;
-
- get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
-
- if (entry && git_config_parse_int32(&val, entry->value) < 0)
- giterr_clear();
-
- git_config_entry_free(entry);
- return (int)val;
-}
-
-int git_config_get_multivar_foreach(
- const git_config *cfg, const char *name, const char *regexp,
- git_config_foreach_cb cb, void *payload)
-{
- int err, found;
- git_config_iterator *iter;
- git_config_entry *entry;
-
- if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0)
- return err;
-
- found = 0;
- while ((err = iter->next(&entry, iter)) == 0) {
- found = 1;
-
- if ((err = cb(entry, payload)) != 0) {
- giterr_set_after_callback(err);
- break;
- }
- }
-
- iter->free(iter);
- if (err == GIT_ITEROVER)
- err = 0;
-
- if (found == 0 && err == 0)
- err = config_error_notfound(name);
-
- return err;
-}
-
-typedef struct {
- git_config_iterator parent;
- git_config_iterator *iter;
- char *name;
- regex_t regex;
- int have_regex;
-} multivar_iter;
-
-static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter)
-{
- multivar_iter *iter = (multivar_iter *) _iter;
- int error = 0;
-
- while ((error = iter->iter->next(entry, iter->iter)) == 0) {
- if (git__strcmp(iter->name, (*entry)->name))
- continue;
-
- if (!iter->have_regex)
- return 0;
-
- if (regexec(&iter->regex, (*entry)->value, 0, NULL, 0) == 0)
- return 0;
- }
-
- return error;
-}
-
-void multivar_iter_free(git_config_iterator *_iter)
-{
- multivar_iter *iter = (multivar_iter *) _iter;
-
- iter->iter->free(iter->iter);
-
- git__free(iter->name);
- if (iter->have_regex)
- regfree(&iter->regex);
- git__free(iter);
-}
-
-int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
-{
- multivar_iter *iter = NULL;
- git_config_iterator *inner = NULL;
- int error;
-
- if ((error = git_config_iterator_new(&inner, cfg)) < 0)
- return error;
-
- iter = git__calloc(1, sizeof(multivar_iter));
- GITERR_CHECK_ALLOC(iter);
-
- if ((error = git_config__normalize_name(name, &iter->name)) < 0)
- goto on_error;
-
- if (regexp != NULL) {
- error = p_regcomp(&iter->regex, regexp, REG_EXTENDED);
- if (error != 0) {
- giterr_set_regex(&iter->regex, error);
- error = -1;
- regfree(&iter->regex);
- goto on_error;
- }
-
- iter->have_regex = 1;
- }
-
- iter->iter = inner;
- iter->parent.free = multivar_iter_free;
- iter->parent.next = multivar_iter_next;
-
- *out = (git_config_iterator *) iter;
-
- return 0;
-
-on_error:
-
- inner->free(inner);
- git__free(iter);
- return error;
-}
-
-int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
-{
- git_config_backend *file;
- file_internal *internal;
-
- internal = git_vector_get(&cfg->files, 0);
- if (!internal || !internal->file)
- return config_error_nofiles(name);
- file = internal->file;
-
- return file->set_multivar(file, name, regexp, value);
-}
-
-int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
-{
- git_config_backend *file;
- file_internal *internal;
-
- internal = git_vector_get(&cfg->files, 0);
- if (!internal || !internal->file)
- return config_error_nofiles(name);
- file = internal->file;
-
- return file->del_multivar(file, name, regexp);
-}
-
-int git_config_next(git_config_entry **entry, git_config_iterator *iter)
-{
- return iter->next(entry, iter);
-}
-
-void git_config_iterator_free(git_config_iterator *iter)
-{
- if (iter == NULL)
- return;
-
- iter->free(iter);
-}
-
-int git_config_find_global(git_buf *path)
-{
- git_buf_sanitize(path);
- return git_sysdir_find_global_file(path, GIT_CONFIG_FILENAME_GLOBAL);
-}
-
-int git_config_find_xdg(git_buf *path)
-{
- git_buf_sanitize(path);
- return git_sysdir_find_xdg_file(path, GIT_CONFIG_FILENAME_XDG);
-}
-
-int git_config_find_system(git_buf *path)
-{
- git_buf_sanitize(path);
- return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
-}
-
-int git_config_find_programdata(git_buf *path)
-{
- git_buf_sanitize(path);
- return git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA);
-}
-
-int git_config__global_location(git_buf *buf)
-{
- const git_buf *paths;
- const char *sep, *start;
-
- if (git_sysdir_get(&paths, GIT_SYSDIR_GLOBAL) < 0)
- return -1;
-
- /* no paths, so give up */
- if (!paths || !git_buf_len(paths))
- return -1;
-
- /* find unescaped separator or end of string */
- for (sep = start = git_buf_cstr(paths); *sep; ++sep) {
- if (*sep == GIT_PATH_LIST_SEPARATOR &&
- (sep <= start || sep[-1] != '\\'))
- break;
- }
-
- if (git_buf_set(buf, start, (size_t)(sep - start)) < 0)
- return -1;
-
- return git_buf_joinpath(buf, buf->ptr, GIT_CONFIG_FILENAME_GLOBAL);
-}
-
-int git_config_open_default(git_config **out)
-{
- int error;
- git_config *cfg = NULL;
- git_buf buf = GIT_BUF_INIT;
-
- if ((error = git_config_new(&cfg)) < 0)
- return error;
-
- if (!git_config_find_global(&buf) || !git_config__global_location(&buf)) {
- error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_GLOBAL, 0);
- }
-
- if (!error && !git_config_find_xdg(&buf))
- error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_XDG, 0);
-
- if (!error && !git_config_find_system(&buf))
- error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_SYSTEM, 0);
-
- if (!error && !git_config_find_programdata(&buf))
- error = git_config_add_file_ondisk(cfg, buf.ptr,
- GIT_CONFIG_LEVEL_PROGRAMDATA, 0);
-
- git_buf_free(&buf);
-
- if (error) {
- git_config_free(cfg);
- cfg = NULL;
- }
-
- *out = cfg;
-
- return error;
-}
-
-int git_config_lock(git_transaction **out, git_config *cfg)
-{
- int error;
- git_config_backend *file;
- file_internal *internal;
-
- internal = git_vector_get(&cfg->files, 0);
- if (!internal || !internal->file) {
- giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files");
- return -1;
- }
- file = internal->file;
-
- if ((error = file->lock(file)) < 0)
- return error;
-
- return git_transaction_config_new(out, cfg);
-}
-
-int git_config_unlock(git_config *cfg, int commit)
-{
- git_config_backend *file;
- file_internal *internal;
-
- internal = git_vector_get(&cfg->files, 0);
- if (!internal || !internal->file) {
- giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends/files");
- return -1;
- }
-
- file = internal->file;
-
- return file->unlock(file, commit);
-}
-
-/***********
- * Parsers
- ***********/
-
-int git_config_lookup_map_value(
- int *out,
- const git_cvar_map *maps,
- size_t map_n,
- const char *value)
-{
- size_t i;
-
- if (!value)
- goto fail_parse;
-
- for (i = 0; i < map_n; ++i) {
- const git_cvar_map *m = maps + i;
-
- switch (m->cvar_type) {
- case GIT_CVAR_FALSE:
- case GIT_CVAR_TRUE: {
- int bool_val;
-
- if (git__parse_bool(&bool_val, value) == 0 &&
- bool_val == (int)m->cvar_type) {
- *out = m->map_value;
- return 0;
- }
- break;
- }
-
- case GIT_CVAR_INT32:
- if (git_config_parse_int32(out, value) == 0)
- return 0;
- break;
-
- case GIT_CVAR_STRING:
- if (strcasecmp(value, m->str_match) == 0) {
- *out = m->map_value;
- return 0;
- }
- break;
- }
- }
-
-fail_parse:
- giterr_set(GITERR_CONFIG, "Failed to map '%s'", value);
- return -1;
-}
-
-int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out,
- const git_cvar_map *maps, size_t map_n, int enum_val)
-{
- size_t i;
-
- for (i = 0; i < map_n; i++) {
- const git_cvar_map *m = &maps[i];
-
- if (m->map_value != enum_val)
- continue;
-
- *type_out = m->cvar_type;
- *str_out = m->str_match;
- return 0;
- }
-
- giterr_set(GITERR_CONFIG, "invalid enum value");
- return GIT_ENOTFOUND;
-}
-
-int git_config_parse_bool(int *out, const char *value)
-{
- if (git__parse_bool(out, value) == 0)
- return 0;
-
- if (git_config_parse_int32(out, value) == 0) {
- *out = !!(*out);
- return 0;
- }
-
- giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);
- return -1;
-}
-
-int git_config_parse_int64(int64_t *out, const char *value)
-{
- const char *num_end;
- int64_t num;
-
- if (!value || git__strtol64(&num, value, &num_end, 0) < 0)
- goto fail_parse;
-
- switch (*num_end) {
- case 'g':
- case 'G':
- num *= 1024;
- /* fallthrough */
-
- case 'm':
- case 'M':
- num *= 1024;
- /* fallthrough */
-
- case 'k':
- case 'K':
- num *= 1024;
-
- /* check that that there are no more characters after the
- * given modifier suffix */
- if (num_end[1] != '\0')
- return -1;
-
- /* fallthrough */
-
- case '\0':
- *out = num;
- return 0;
-
- default:
- goto fail_parse;
- }
-
-fail_parse:
- giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value ? value : "(null)");
- return -1;
-}
-
-int git_config_parse_int32(int32_t *out, const char *value)
-{
- int64_t tmp;
- int32_t truncate;
-
- if (git_config_parse_int64(&tmp, value) < 0)
- goto fail_parse;
-
- truncate = tmp & 0xFFFFFFFF;
- if (truncate != tmp)
- goto fail_parse;
-
- *out = truncate;
- return 0;
-
-fail_parse:
- giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value ? value : "(null)");
- return -1;
-}
-
-int git_config_parse_path(git_buf *out, const char *value)
-{
- int error = 0;
- const git_buf *home;
-
- assert(out && value);
-
- git_buf_sanitize(out);
-
- if (value[0] == '~') {
- if (value[1] != '\0' && value[1] != '/') {
- giterr_set(GITERR_CONFIG, "retrieving a homedir by name is not supported");
- return -1;
- }
-
- if ((error = git_sysdir_get(&home, GIT_SYSDIR_GLOBAL)) < 0)
- return error;
-
- git_buf_sets(out, home->ptr);
- git_buf_puts(out, value + 1);
-
- if (git_buf_oom(out))
- return -1;
-
- return 0;
- }
-
- return git_buf_sets(out, value);
-}
-
-/* Take something the user gave us and make it nice for our hash function */
-int git_config__normalize_name(const char *in, char **out)
-{
- char *name, *fdot, *ldot;
-
- assert(in && out);
-
- name = git__strdup(in);
- GITERR_CHECK_ALLOC(name);
-
- fdot = strchr(name, '.');
- ldot = strrchr(name, '.');
-
- if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
- goto invalid;
-
- /* Validate and downcase up to first dot and after last dot */
- if (git_config_file_normalize_section(name, fdot) < 0 ||
- git_config_file_normalize_section(ldot + 1, NULL) < 0)
- goto invalid;
-
- /* If there is a middle range, make sure it doesn't have newlines */
- while (fdot < ldot)
- if (*fdot++ == '\n')
- goto invalid;
-
- *out = name;
- return 0;
-
-invalid:
- git__free(name);
- giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
- return GIT_EINVALIDSPEC;
-}
-
-struct rename_data {
- git_config *config;
- git_buf *name;
- size_t old_len;
-};
-
-static int rename_config_entries_cb(
- const git_config_entry *entry,
- void *payload)
-{
- int error = 0;
- struct rename_data *data = (struct rename_data *)payload;
- size_t base_len = git_buf_len(data->name);
-
- if (base_len > 0 &&
- !(error = git_buf_puts(data->name, entry->name + data->old_len)))
- {
- error = git_config_set_string(
- data->config, git_buf_cstr(data->name), entry->value);
-
- git_buf_truncate(data->name, base_len);
- }
-
- if (!error)
- error = git_config_delete_entry(data->config, entry->name);
-
- return error;
-}
-
-int git_config_rename_section(
- git_repository *repo,
- const char *old_section_name,
- const char *new_section_name)
-{
- git_config *config;
- git_buf pattern = GIT_BUF_INIT, replace = GIT_BUF_INIT;
- int error = 0;
- struct rename_data data;
-
- git_buf_text_puts_escape_regex(&pattern, old_section_name);
-
- if ((error = git_buf_puts(&pattern, "\\..+")) < 0)
- goto cleanup;
-
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
- goto cleanup;
-
- data.config = config;
- data.name = &replace;
- data.old_len = strlen(old_section_name) + 1;
-
- if ((error = git_buf_join(&replace, '.', new_section_name, "")) < 0)
- goto cleanup;
-
- if (new_section_name != NULL &&
- (error = git_config_file_normalize_section(
- replace.ptr, strchr(replace.ptr, '.'))) < 0)
- {
- giterr_set(
- GITERR_CONFIG, "Invalid config section '%s'", new_section_name);
- goto cleanup;
- }
-
- error = git_config_foreach_match(
- config, git_buf_cstr(&pattern), rename_config_entries_cb, &data);
-
-cleanup:
- git_buf_free(&pattern);
- git_buf_free(&replace);
-
- return error;
-}
-
-int git_config_init_backend(git_config_backend *backend, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- backend, version, git_config_backend, GIT_CONFIG_BACKEND_INIT);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_config_h__
-#define INCLUDE_config_h__
-
-#include "git2.h"
-#include "git2/config.h"
-#include "vector.h"
-#include "repository.h"
-
-#define GIT_CONFIG_FILENAME_PROGRAMDATA "config"
-#define GIT_CONFIG_FILENAME_SYSTEM "gitconfig"
-#define GIT_CONFIG_FILENAME_GLOBAL ".gitconfig"
-#define GIT_CONFIG_FILENAME_XDG "config"
-
-#define GIT_CONFIG_FILENAME_INREPO "config"
-#define GIT_CONFIG_FILE_MODE 0666
-
-struct git_config {
- git_refcount rc;
- git_vector files;
-};
-
-extern int git_config__global_location(git_buf *buf);
-
-extern int git_config_rename_section(
- git_repository *repo,
- const char *old_section_name, /* eg "branch.dummy" */
- const char *new_section_name); /* NULL to drop the old section */
-
-/**
- * Create a configuration file backend for ondisk files
- *
- * These are the normal `.gitconfig` files that Core Git
- * processes. Note that you first have to add this file to a
- * configuration object before you can query it for configuration
- * variables.
- *
- * @param out the new backend
- * @param path where the config file is located
- */
-extern int git_config_file__ondisk(git_config_backend **out, const char *path);
-
-extern int git_config__normalize_name(const char *in, char **out);
-
-/* internal only: does not normalize key and sets out to NULL if not found */
-extern int git_config__lookup_entry(
- git_config_entry **out,
- const git_config *cfg,
- const char *key,
- bool no_errors);
-
-/* internal only: update and/or delete entry string with constraints */
-extern int git_config__update_entry(
- git_config *cfg,
- const char *key,
- const char *value,
- bool overwrite_existing,
- bool only_if_existing);
-
-/*
- * Lookup functions that cannot fail. These functions look up a config
- * value and return a fallback value if the value is missing or if any
- * failures occur while trying to access the value.
- */
-
-extern char *git_config__get_string_force(
- const git_config *cfg, const char *key, const char *fallback_value);
-
-extern int git_config__get_bool_force(
- const git_config *cfg, const char *key, int fallback_value);
-
-extern int git_config__get_int_force(
- const git_config *cfg, const char *key, int fallback_value);
-
-/* API for repository cvar-style lookups from config - not cached, but
- * uses cvar value maps and fallbacks
- */
-extern int git_config__cvar(
- int *out, git_config *config, git_cvar_cached cvar);
-
-/**
- * The opposite of git_config_lookup_map_value, we take an enum value
- * and map it to the string or bool value on the config.
- */
-int git_config_lookup_map_enum(git_cvar_t *type_out, const char **str_out,
- const git_cvar_map *maps, size_t map_n, int enum_val);
-
-/**
- * Unlock the backend with the highest priority
- *
- * Unlocking will allow other writers to updat the configuration
- * file. Optionally, any changes performed since the lock will be
- * applied to the configuration.
- *
- * @param cfg the configuration
- * @param commit boolean which indicates whether to commit any changes
- * done since locking
- * @return 0 or an error code
- */
-GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "fileops.h"
-#include "repository.h"
-#include "config.h"
-#include "git2/config.h"
-#include "vector.h"
-#include "filter.h"
-
-struct map_data {
- const char *cvar_name;
- git_cvar_map *maps;
- size_t map_count;
- int default_value;
-};
-
-/*
- * core.eol
- * Sets the line ending type to use in the working directory for
- * files that have the text property set. Alternatives are lf, crlf
- * and native, which uses the platform's native line ending. The default
- * value is native. See gitattributes(5) for more information on
- * end-of-line conversion.
- */
-static git_cvar_map _cvar_map_eol[] = {
- {GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
- {GIT_CVAR_STRING, "lf", GIT_EOL_LF},
- {GIT_CVAR_STRING, "crlf", GIT_EOL_CRLF},
- {GIT_CVAR_STRING, "native", GIT_EOL_NATIVE}
-};
-
-/*
- * core.autocrlf
- * Setting this variable to "true" is almost the same as setting
- * the text attribute to "auto" on all files except that text files are
- * not guaranteed to be normalized: files that contain CRLF in the
- * repository will not be touched. Use this setting if you want to have
- * CRLF line endings in your working directory even though the repository
- * does not have normalized line endings. This variable can be set to input,
- * in which case no output conversion is performed.
- */
-static git_cvar_map _cvar_map_autocrlf[] = {
- {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
- {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
- {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
-};
-
-static git_cvar_map _cvar_map_safecrlf[] = {
- {GIT_CVAR_FALSE, NULL, GIT_SAFE_CRLF_FALSE},
- {GIT_CVAR_TRUE, NULL, GIT_SAFE_CRLF_FAIL},
- {GIT_CVAR_STRING, "warn", GIT_SAFE_CRLF_WARN}
-};
-
-/*
- * Generic map for integer values
- */
-static git_cvar_map _cvar_map_int[] = {
- {GIT_CVAR_INT32, NULL, 0},
-};
-
-static struct map_data _cvar_maps[] = {
- {"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},
- {"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT},
- {"core.symlinks", NULL, 0, GIT_SYMLINKS_DEFAULT },
- {"core.ignorecase", NULL, 0, GIT_IGNORECASE_DEFAULT },
- {"core.filemode", NULL, 0, GIT_FILEMODE_DEFAULT },
- {"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT },
- {"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
- {"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
- {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
- {"core.safecrlf", _cvar_map_safecrlf, ARRAY_SIZE(_cvar_map_safecrlf), GIT_SAFE_CRLF_DEFAULT},
- {"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT },
- {"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT },
- {"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT },
-};
-
-int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar)
-{
- int error = 0;
- struct map_data *data = &_cvar_maps[(int)cvar];
- git_config_entry *entry;
-
- if ((error = git_config__lookup_entry(&entry, config, data->cvar_name, false)) < 0)
- return error;
-
- if (!entry)
- *out = data->default_value;
- else if (data->maps)
- error = git_config_lookup_map_value(
- out, data->maps, data->map_count, entry->value);
- else
- error = git_config_parse_bool(out, entry->value);
-
- git_config_entry_free(entry);
- return error;
-}
-
-int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
-{
- *out = repo->cvar_cache[(int)cvar];
-
- if (*out == GIT_CVAR_NOT_CACHED) {
- int error;
- git_config *config;
-
- if ((error = git_repository_config__weakptr(&config, repo)) < 0 ||
- (error = git_config__cvar(out, config, cvar)) < 0)
- return error;
-
- repo->cvar_cache[(int)cvar] = *out;
- }
-
- return 0;
-}
-
-void git_repository__cvar_cache_clear(git_repository *repo)
-{
- int i;
-
- for (i = 0; i < GIT_CVAR_CACHE_MAX; ++i)
- repo->cvar_cache[i] = GIT_CVAR_NOT_CACHED;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "config.h"
-#include "filebuf.h"
-#include "sysdir.h"
-#include "buffer.h"
-#include "buf_text.h"
-#include "git2/config.h"
-#include "git2/sys/config.h"
-#include "git2/types.h"
-#include "strmap.h"
-#include "array.h"
-
-#include <ctype.h>
-#include <sys/types.h>
-#include <regex.h>
-
-GIT__USE_STRMAP
-
-typedef struct cvar_t {
- struct cvar_t *next;
- git_config_entry *entry;
- bool included; /* whether this is part of [include] */
-} cvar_t;
-
-typedef struct git_config_file_iter {
- git_config_iterator parent;
- git_strmap_iter iter;
- cvar_t* next_var;
-} git_config_file_iter;
-
-/* Max depth for [include] directives */
-#define MAX_INCLUDE_DEPTH 10
-
-#define CVAR_LIST_HEAD(list) ((list)->head)
-
-#define CVAR_LIST_TAIL(list) ((list)->tail)
-
-#define CVAR_LIST_NEXT(var) ((var)->next)
-
-#define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
-
-#define CVAR_LIST_APPEND(list, var) do {\
- if (CVAR_LIST_EMPTY(list)) {\
- CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
- } else {\
- CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
- CVAR_LIST_TAIL(list) = var;\
- }\
-} while(0)
-
-#define CVAR_LIST_REMOVE_HEAD(list) do {\
- CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
-} while(0)
-
-#define CVAR_LIST_REMOVE_AFTER(var) do {\
- CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
-} while(0)
-
-#define CVAR_LIST_FOREACH(list, iter)\
- for ((iter) = CVAR_LIST_HEAD(list);\
- (iter) != NULL;\
- (iter) = CVAR_LIST_NEXT(iter))
-
-/*
- * Inspired by the FreeBSD functions
- */
-#define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
- for ((iter) = CVAR_LIST_HEAD(vars);\
- (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
- (iter) = (tmp))
-
-struct reader {
- git_oid checksum;
- char *file_path;
- git_buf buffer;
- char *read_ptr;
- int line_number;
- int eof;
-};
-
-typedef struct {
- git_atomic refcount;
- git_strmap *values;
-} refcounted_strmap;
-
-typedef struct {
- git_config_backend parent;
- /* mutex to coordinate accessing the values */
- git_mutex values_mutex;
- refcounted_strmap *values;
-} diskfile_header;
-
-typedef struct {
- diskfile_header header;
-
- git_config_level_t level;
-
- git_array_t(struct reader) readers;
-
- bool locked;
- git_filebuf locked_buf;
- git_buf locked_content;
-
- char *file_path;
-} diskfile_backend;
-
-typedef struct {
- diskfile_header header;
-
- diskfile_backend *snapshot_from;
-} diskfile_readonly_backend;
-
-static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
-static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
-static char *escape_value(const char *ptr);
-
-int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in);
-static int config_snapshot(git_config_backend **out, git_config_backend *in);
-
-static void set_parse_error(struct reader *reader, int col, const char *error_str)
-{
- giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
- error_str, reader->file_path, reader->line_number, col);
-}
-
-static int config_error_readonly(void)
-{
- giterr_set(GITERR_CONFIG, "this backend is read-only");
- return -1;
-}
-
-static void cvar_free(cvar_t *var)
-{
- if (var == NULL)
- return;
-
- git__free((char*)var->entry->name);
- git__free((char *)var->entry->value);
- git__free(var->entry);
- git__free(var);
-}
-
-int git_config_file_normalize_section(char *start, char *end)
-{
- char *scan;
-
- if (start == end)
- return GIT_EINVALIDSPEC;
-
- /* Validate and downcase range */
- for (scan = start; *scan; ++scan) {
- if (end && scan >= end)
- break;
- if (isalnum(*scan))
- *scan = (char)git__tolower(*scan);
- else if (*scan != '-' || scan == start)
- return GIT_EINVALIDSPEC;
- }
-
- if (scan == start)
- return GIT_EINVALIDSPEC;
-
- return 0;
-}
-
-/* Add or append the new config option */
-static int append_entry(git_strmap *values, cvar_t *var)
-{
- git_strmap_iter pos;
- cvar_t *existing;
- int error = 0;
-
- pos = git_strmap_lookup_index(values, var->entry->name);
- if (!git_strmap_valid_index(values, pos)) {
- git_strmap_insert(values, var->entry->name, var, error);
- } else {
- existing = git_strmap_value_at(values, pos);
- while (existing->next != NULL) {
- existing = existing->next;
- }
- existing->next = var;
- }
-
- if (error > 0)
- error = 0;
-
- return error;
-}
-
-static void free_vars(git_strmap *values)
-{
- cvar_t *var = NULL;
-
- if (values == NULL)
- return;
-
- git_strmap_foreach_value(values, var,
- while (var != NULL) {
- cvar_t *next = CVAR_LIST_NEXT(var);
- cvar_free(var);
- var = next;
- });
-
- git_strmap_free(values);
-}
-
-static void refcounted_strmap_free(refcounted_strmap *map)
-{
- if (!map)
- return;
-
- if (git_atomic_dec(&map->refcount) != 0)
- return;
-
- free_vars(map->values);
- git__free(map);
-}
-
-/**
- * Take the current values map from the backend and increase its
- * refcount. This is its own function to make sure we use the mutex to
- * avoid the map pointer from changing under us.
- */
-static refcounted_strmap *refcounted_strmap_take(diskfile_header *h)
-{
- refcounted_strmap *map;
-
- if (git_mutex_lock(&h->values_mutex) < 0) {
- giterr_set(GITERR_OS, "Failed to lock config backend");
- return NULL;
- }
-
- map = h->values;
- git_atomic_inc(&map->refcount);
-
- git_mutex_unlock(&h->values_mutex);
-
- return map;
-}
-
-static int refcounted_strmap_alloc(refcounted_strmap **out)
-{
- refcounted_strmap *map;
- int error;
-
- map = git__calloc(1, sizeof(refcounted_strmap));
- GITERR_CHECK_ALLOC(map);
-
- git_atomic_set(&map->refcount, 1);
-
- if ((error = git_strmap_alloc(&map->values)) < 0)
- git__free(map);
- else
- *out = map;
-
- return error;
-}
-
-static int config_open(git_config_backend *cfg, git_config_level_t level)
-{
- int res;
- struct reader *reader;
- diskfile_backend *b = (diskfile_backend *)cfg;
-
- b->level = level;
-
- if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
- return res;
-
- git_array_init(b->readers);
- reader = git_array_alloc(b->readers);
- if (!reader) {
- refcounted_strmap_free(b->header.values);
- return -1;
- }
- memset(reader, 0, sizeof(struct reader));
-
- reader->file_path = git__strdup(b->file_path);
- GITERR_CHECK_ALLOC(reader->file_path);
-
- git_buf_init(&reader->buffer, 0);
- res = git_futils_readbuffer_updated(
- &reader->buffer, b->file_path, &reader->checksum, NULL);
-
- /* It's fine if the file doesn't exist */
- if (res == GIT_ENOTFOUND)
- return 0;
-
- if (res < 0 || (res = config_read(b->header.values->values, b, reader, level, 0)) < 0) {
- refcounted_strmap_free(b->header.values);
- b->header.values = NULL;
- }
-
- reader = git_array_get(b->readers, 0);
- git_buf_free(&reader->buffer);
-
- return res;
-}
-
-/* The meat of the refresh, as we want to use it in different places */
-static int config__refresh(git_config_backend *cfg)
-{
- refcounted_strmap *values = NULL, *tmp;
- diskfile_backend *b = (diskfile_backend *)cfg;
- struct reader *reader = NULL;
- int error = 0;
-
- if ((error = refcounted_strmap_alloc(&values)) < 0)
- goto out;
-
- reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
- GITERR_CHECK_ALLOC(reader);
-
- if ((error = config_read(values->values, b, reader, b->level, 0)) < 0)
- goto out;
-
- if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
- giterr_set(GITERR_OS, "Failed to lock config backend");
- goto out;
- }
-
- tmp = b->header.values;
- b->header.values = values;
- values = tmp;
-
- git_mutex_unlock(&b->header.values_mutex);
-
-out:
- refcounted_strmap_free(values);
- if (reader)
- git_buf_free(&reader->buffer);
- return error;
-}
-
-static int config_refresh(git_config_backend *cfg)
-{
- int error = 0, updated = 0, any_updated = 0;
- diskfile_backend *b = (diskfile_backend *)cfg;
- struct reader *reader = NULL;
- uint32_t i;
-
- for (i = 0; i < git_array_size(b->readers); i++) {
- reader = git_array_get(b->readers, i);
- error = git_futils_readbuffer_updated(
- &reader->buffer, reader->file_path,
- &reader->checksum, &updated);
-
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
-
- if (updated)
- any_updated = 1;
- }
-
- if (!any_updated)
- return (error == GIT_ENOTFOUND) ? 0 : error;
-
- return config__refresh(cfg);
-}
-
-static void backend_free(git_config_backend *_backend)
-{
- diskfile_backend *backend = (diskfile_backend *)_backend;
- uint32_t i;
-
- if (backend == NULL)
- return;
-
- for (i = 0; i < git_array_size(backend->readers); i++) {
- struct reader *r = git_array_get(backend->readers, i);
- git__free(r->file_path);
- }
- git_array_clear(backend->readers);
-
- git__free(backend->file_path);
- refcounted_strmap_free(backend->header.values);
- git_mutex_free(&backend->header.values_mutex);
- git__free(backend);
-}
-
-static void config_iterator_free(
- git_config_iterator* iter)
-{
- iter->backend->free(iter->backend);
- git__free(iter);
-}
-
-static int config_iterator_next(
- git_config_entry **entry,
- git_config_iterator *iter)
-{
- git_config_file_iter *it = (git_config_file_iter *) iter;
- diskfile_header *h = (diskfile_header *) it->parent.backend;
- git_strmap *values = h->values->values;
- int err = 0;
- cvar_t * var;
-
- if (it->next_var == NULL) {
- err = git_strmap_next((void**) &var, &(it->iter), values);
- } else {
- var = it->next_var;
- }
-
- if (err < 0) {
- it->next_var = NULL;
- return err;
- }
-
- *entry = var->entry;
- it->next_var = CVAR_LIST_NEXT(var);
-
- return 0;
-}
-
-static int config_iterator_new(
- git_config_iterator **iter,
- struct git_config_backend* backend)
-{
- diskfile_header *h;
- git_config_file_iter *it;
- git_config_backend *snapshot;
- diskfile_backend *b = (diskfile_backend *) backend;
- int error;
-
- if ((error = config_snapshot(&snapshot, backend)) < 0)
- return error;
-
- if ((error = snapshot->open(snapshot, b->level)) < 0)
- return error;
-
- it = git__calloc(1, sizeof(git_config_file_iter));
- GITERR_CHECK_ALLOC(it);
-
- h = (diskfile_header *)snapshot;
-
- /* strmap_begin() is currently a macro returning 0 */
- GIT_UNUSED(h);
-
- it->parent.backend = snapshot;
- it->iter = git_strmap_begin(h->values);
- it->next_var = NULL;
-
- it->parent.next = config_iterator_next;
- it->parent.free = config_iterator_free;
- *iter = (git_config_iterator *) it;
-
- return 0;
-}
-
-static int config_set(git_config_backend *cfg, const char *name, const char *value)
-{
- diskfile_backend *b = (diskfile_backend *)cfg;
- refcounted_strmap *map;
- git_strmap *values;
- char *key, *esc_value = NULL;
- khiter_t pos;
- int rval, ret;
-
- if ((rval = git_config__normalize_name(name, &key)) < 0)
- return rval;
-
- if ((map = refcounted_strmap_take(&b->header)) == NULL)
- return -1;
- values = map->values;
-
- /*
- * Try to find it in the existing values and update it if it
- * only has one value.
- */
- pos = git_strmap_lookup_index(values, key);
- if (git_strmap_valid_index(values, pos)) {
- cvar_t *existing = git_strmap_value_at(values, pos);
-
- if (existing->next != NULL) {
- giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
- ret = -1;
- goto out;
- }
-
- /* don't update if old and new values already match */
- if ((!existing->entry->value && !value) ||
- (existing->entry->value && value &&
- !strcmp(existing->entry->value, value))) {
- ret = 0;
- goto out;
- }
- }
-
- /* No early returns due to sanity checks, let's write it out and refresh */
-
- if (value) {
- esc_value = escape_value(value);
- GITERR_CHECK_ALLOC(esc_value);
- }
-
- if ((ret = config_write(b, key, NULL, esc_value)) < 0)
- goto out;
-
- ret = config_refresh(cfg);
-
-out:
- refcounted_strmap_free(map);
- git__free(esc_value);
- git__free(key);
- return ret;
-}
-
-/* release the map containing the entry as an equivalent to freeing it */
-static void release_map(git_config_entry *entry)
-{
- refcounted_strmap *map = (refcounted_strmap *) entry->payload;
- refcounted_strmap_free(map);
-}
-
-/*
- * Internal function that actually gets the value in string form
- */
-static int config_get(git_config_backend *cfg, const char *key, git_config_entry **out)
-{
- diskfile_header *h = (diskfile_header *)cfg;
- refcounted_strmap *map;
- git_strmap *values;
- khiter_t pos;
- cvar_t *var;
- int error = 0;
-
- if (!h->parent.readonly && ((error = config_refresh(cfg)) < 0))
- return error;
-
- if ((map = refcounted_strmap_take(h)) == NULL)
- return -1;
- values = map->values;
-
- pos = git_strmap_lookup_index(values, key);
-
- /* no error message; the config system will write one */
- if (!git_strmap_valid_index(values, pos)) {
- refcounted_strmap_free(map);
- return GIT_ENOTFOUND;
- }
-
- var = git_strmap_value_at(values, pos);
- while (var->next)
- var = var->next;
-
- *out = var->entry;
- (*out)->free = release_map;
- (*out)->payload = map;
-
- return error;
-}
-
-static int config_set_multivar(
- git_config_backend *cfg, const char *name, const char *regexp, const char *value)
-{
- diskfile_backend *b = (diskfile_backend *)cfg;
- char *key;
- regex_t preg;
- int result;
-
- assert(regexp);
-
- if ((result = git_config__normalize_name(name, &key)) < 0)
- return result;
-
- result = p_regcomp(&preg, regexp, REG_EXTENDED);
- if (result != 0) {
- giterr_set_regex(&preg, result);
- result = -1;
- goto out;
- }
-
- /* If we do have it, set call config_write() and reload */
- if ((result = config_write(b, key, &preg, value)) < 0)
- goto out;
-
- result = config_refresh(cfg);
-
-out:
- git__free(key);
- regfree(&preg);
-
- return result;
-}
-
-static int config_delete(git_config_backend *cfg, const char *name)
-{
- cvar_t *var;
- diskfile_backend *b = (diskfile_backend *)cfg;
- refcounted_strmap *map; git_strmap *values;
- char *key;
- int result;
- khiter_t pos;
-
- if ((result = git_config__normalize_name(name, &key)) < 0)
- return result;
-
- if ((map = refcounted_strmap_take(&b->header)) == NULL)
- return -1;
- values = b->header.values->values;
-
- pos = git_strmap_lookup_index(values, key);
- git__free(key);
-
- if (!git_strmap_valid_index(values, pos)) {
- refcounted_strmap_free(map);
- giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
- return GIT_ENOTFOUND;
- }
-
- var = git_strmap_value_at(values, pos);
- refcounted_strmap_free(map);
-
- if (var->next != NULL) {
- giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
- return -1;
- }
-
- if ((result = config_write(b, var->entry->name, NULL, NULL)) < 0)
- return result;
-
- return config_refresh(cfg);
-}
-
-static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
-{
- diskfile_backend *b = (diskfile_backend *)cfg;
- refcounted_strmap *map;
- git_strmap *values;
- char *key;
- regex_t preg;
- int result;
- khiter_t pos;
-
- if ((result = git_config__normalize_name(name, &key)) < 0)
- return result;
-
- if ((map = refcounted_strmap_take(&b->header)) == NULL)
- return -1;
- values = b->header.values->values;
-
- pos = git_strmap_lookup_index(values, key);
-
- if (!git_strmap_valid_index(values, pos)) {
- refcounted_strmap_free(map);
- git__free(key);
- giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
- return GIT_ENOTFOUND;
- }
-
- refcounted_strmap_free(map);
-
- result = p_regcomp(&preg, regexp, REG_EXTENDED);
- if (result != 0) {
- giterr_set_regex(&preg, result);
- result = -1;
- goto out;
- }
-
- if ((result = config_write(b, key, &preg, NULL)) < 0)
- goto out;
-
- result = config_refresh(cfg);
-
-out:
- git__free(key);
- regfree(&preg);
- return result;
-}
-
-static int config_snapshot(git_config_backend **out, git_config_backend *in)
-{
- diskfile_backend *b = (diskfile_backend *) in;
-
- return git_config_file__snapshot(out, b);
-}
-
-static int config_lock(git_config_backend *_cfg)
-{
- diskfile_backend *cfg = (diskfile_backend *) _cfg;
- int error;
-
- if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0)
- return error;
-
- error = git_futils_readbuffer(&cfg->locked_content, cfg->file_path);
- if (error < 0 && error != GIT_ENOTFOUND) {
- git_filebuf_cleanup(&cfg->locked_buf);
- return error;
- }
-
- cfg->locked = true;
- return 0;
-
-}
-
-static int config_unlock(git_config_backend *_cfg, int success)
-{
- diskfile_backend *cfg = (diskfile_backend *) _cfg;
- int error = 0;
-
- if (success) {
- git_filebuf_write(&cfg->locked_buf, cfg->locked_content.ptr, cfg->locked_content.size);
- error = git_filebuf_commit(&cfg->locked_buf);
- }
-
- git_filebuf_cleanup(&cfg->locked_buf);
- git_buf_free(&cfg->locked_content);
- cfg->locked = false;
-
- return error;
-}
-
-int git_config_file__ondisk(git_config_backend **out, const char *path)
-{
- diskfile_backend *backend;
-
- backend = git__calloc(1, sizeof(diskfile_backend));
- GITERR_CHECK_ALLOC(backend);
-
- backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
- git_mutex_init(&backend->header.values_mutex);
-
- backend->file_path = git__strdup(path);
- GITERR_CHECK_ALLOC(backend->file_path);
-
- backend->header.parent.open = config_open;
- backend->header.parent.get = config_get;
- backend->header.parent.set = config_set;
- backend->header.parent.set_multivar = config_set_multivar;
- backend->header.parent.del = config_delete;
- backend->header.parent.del_multivar = config_delete_multivar;
- backend->header.parent.iterator = config_iterator_new;
- backend->header.parent.snapshot = config_snapshot;
- backend->header.parent.lock = config_lock;
- backend->header.parent.unlock = config_unlock;
- backend->header.parent.free = backend_free;
-
- *out = (git_config_backend *)backend;
-
- return 0;
-}
-
-static int config_set_readonly(git_config_backend *cfg, const char *name, const char *value)
-{
- GIT_UNUSED(cfg);
- GIT_UNUSED(name);
- GIT_UNUSED(value);
-
- return config_error_readonly();
-}
-
-static int config_set_multivar_readonly(
- git_config_backend *cfg, const char *name, const char *regexp, const char *value)
-{
- GIT_UNUSED(cfg);
- GIT_UNUSED(name);
- GIT_UNUSED(regexp);
- GIT_UNUSED(value);
-
- return config_error_readonly();
-}
-
-static int config_delete_multivar_readonly(git_config_backend *cfg, const char *name, const char *regexp)
-{
- GIT_UNUSED(cfg);
- GIT_UNUSED(name);
- GIT_UNUSED(regexp);
-
- return config_error_readonly();
-}
-
-static int config_delete_readonly(git_config_backend *cfg, const char *name)
-{
- GIT_UNUSED(cfg);
- GIT_UNUSED(name);
-
- return config_error_readonly();
-}
-
-static int config_lock_readonly(git_config_backend *_cfg)
-{
- GIT_UNUSED(_cfg);
-
- return config_error_readonly();
-}
-
-static int config_unlock_readonly(git_config_backend *_cfg, int success)
-{
- GIT_UNUSED(_cfg);
- GIT_UNUSED(success);
-
- return config_error_readonly();
-}
-
-static void backend_readonly_free(git_config_backend *_backend)
-{
- diskfile_backend *backend = (diskfile_backend *)_backend;
-
- if (backend == NULL)
- return;
-
- refcounted_strmap_free(backend->header.values);
- git_mutex_free(&backend->header.values_mutex);
- git__free(backend);
-}
-
-static int config_readonly_open(git_config_backend *cfg, git_config_level_t level)
-{
- diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
- diskfile_backend *src = b->snapshot_from;
- diskfile_header *src_header = &src->header;
- refcounted_strmap *src_map;
- int error;
-
- if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0)
- return error;
-
- /* We're just copying data, don't care about the level */
- GIT_UNUSED(level);
-
- if ((src_map = refcounted_strmap_take(src_header)) == NULL)
- return -1;
- b->header.values = src_map;
-
- return 0;
-}
-
-int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
-{
- diskfile_readonly_backend *backend;
-
- backend = git__calloc(1, sizeof(diskfile_readonly_backend));
- GITERR_CHECK_ALLOC(backend);
-
- backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
- git_mutex_init(&backend->header.values_mutex);
-
- backend->snapshot_from = in;
-
- backend->header.parent.readonly = 1;
- backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
- backend->header.parent.open = config_readonly_open;
- backend->header.parent.get = config_get;
- backend->header.parent.set = config_set_readonly;
- backend->header.parent.set_multivar = config_set_multivar_readonly;
- backend->header.parent.del = config_delete_readonly;
- backend->header.parent.del_multivar = config_delete_multivar_readonly;
- backend->header.parent.iterator = config_iterator_new;
- backend->header.parent.lock = config_lock_readonly;
- backend->header.parent.unlock = config_unlock_readonly;
- backend->header.parent.free = backend_readonly_free;
-
- *out = (git_config_backend *)backend;
-
- return 0;
-}
-
-static int reader_getchar_raw(struct reader *reader)
-{
- int c;
-
- c = *reader->read_ptr++;
-
- /*
- Win 32 line breaks: if we find a \r\n sequence,
- return only the \n as a newline
- */
- if (c == '\r' && *reader->read_ptr == '\n') {
- reader->read_ptr++;
- c = '\n';
- }
-
- if (c == '\n')
- reader->line_number++;
-
- if (c == 0) {
- reader->eof = 1;
- c = '\0';
- }
-
- return c;
-}
-
-#define SKIP_WHITESPACE (1 << 1)
-#define SKIP_COMMENTS (1 << 2)
-
-static int reader_getchar(struct reader *reader, int flags)
-{
- const int skip_whitespace = (flags & SKIP_WHITESPACE);
- const int skip_comments = (flags & SKIP_COMMENTS);
- int c;
-
- assert(reader->read_ptr);
-
- do {
- c = reader_getchar_raw(reader);
- } while (c != '\n' && c != '\0' && skip_whitespace && git__isspace(c));
-
- if (skip_comments && (c == '#' || c == ';')) {
- do {
- c = reader_getchar_raw(reader);
- } while (c != '\n' && c != '\0');
- }
-
- return c;
-}
-
-/*
- * Read the next char, but don't move the reading pointer.
- */
-static int reader_peek(struct reader *reader, int flags)
-{
- void *old_read_ptr;
- int old_lineno, old_eof;
- int ret;
-
- assert(reader->read_ptr);
-
- old_read_ptr = reader->read_ptr;
- old_lineno = reader->line_number;
- old_eof = reader->eof;
-
- ret = reader_getchar(reader, flags);
-
- reader->read_ptr = old_read_ptr;
- reader->line_number = old_lineno;
- reader->eof = old_eof;
-
- return ret;
-}
-
-/*
- * Read and consume a line, returning it in newly-allocated memory.
- */
-static char *reader_readline(struct reader *reader, bool skip_whitespace)
-{
- char *line = NULL;
- char *line_src, *line_end;
- size_t line_len, alloc_len;
-
- line_src = reader->read_ptr;
-
- if (skip_whitespace) {
- /* Skip empty empty lines */
- while (git__isspace(*line_src))
- ++line_src;
- }
-
- line_end = strchr(line_src, '\n');
-
- /* no newline at EOF */
- if (line_end == NULL)
- line_end = strchr(line_src, 0);
-
- line_len = line_end - line_src;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, line_len, 1) ||
- (line = git__malloc(alloc_len)) == NULL) {
- return NULL;
- }
-
- memcpy(line, line_src, line_len);
-
- do line[line_len] = '\0';
- while (line_len-- > 0 && git__isspace(line[line_len]));
-
- if (*line_end == '\n')
- line_end++;
-
- if (*line_end == '\0')
- reader->eof = 1;
-
- reader->line_number++;
- reader->read_ptr = line_end;
-
- return line;
-}
-
-/*
- * Consume a line, without storing it anywhere
- */
-static void reader_consume_line(struct reader *reader)
-{
- char *line_start, *line_end;
-
- line_start = reader->read_ptr;
- line_end = strchr(line_start, '\n');
- /* No newline at EOF */
- if(line_end == NULL){
- line_end = strchr(line_start, '\0');
- }
-
- if (*line_end == '\n')
- line_end++;
-
- if (*line_end == '\0')
- reader->eof = 1;
-
- reader->line_number++;
- reader->read_ptr = line_end;
-}
-
-GIT_INLINE(int) config_keychar(int c)
-{
- return isalnum(c) || c == '-';
-}
-
-static int parse_section_header_ext(struct reader *reader, const char *line, const char *base_name, char **section_name)
-{
- int c, rpos;
- char *first_quote, *last_quote;
- git_buf buf = GIT_BUF_INIT;
- size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
-
- /*
- * base_name is what came before the space. We should be at the
- * first quotation mark, except for now, line isn't being kept in
- * sync so we only really use it to calculate the length.
- */
-
- first_quote = strchr(line, '"');
- if (first_quote == NULL) {
- set_parse_error(reader, 0, "Missing quotation marks in section header");
- return -1;
- }
-
- last_quote = strrchr(line, '"');
- quoted_len = last_quote - first_quote;
-
- if (quoted_len == 0) {
- set_parse_error(reader, 0, "Missing closing quotation mark in section header");
- return -1;
- }
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
-
- git_buf_grow(&buf, alloc_len);
- git_buf_printf(&buf, "%s.", base_name);
-
- rpos = 0;
-
- line = first_quote;
- c = line[++rpos];
-
- /*
- * At the end of each iteration, whatever is stored in c will be
- * added to the string. In case of error, jump to out
- */
- do {
-
- switch (c) {
- case 0:
- set_parse_error(reader, 0, "Unexpected end-of-line in section header");
- git_buf_free(&buf);
- return -1;
-
- case '"':
- goto end_parse;
-
- case '\\':
- c = line[++rpos];
-
- if (c == 0) {
- set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
- git_buf_free(&buf);
- return -1;
- }
-
- default:
- break;
- }
-
- git_buf_putc(&buf, (char)c);
- c = line[++rpos];
- } while (line + rpos < last_quote);
-
-end_parse:
- if (line[rpos] != '"' || line[rpos + 1] != ']') {
- set_parse_error(reader, rpos, "Unexpected text after closing quotes");
- git_buf_free(&buf);
- return -1;
- }
-
- *section_name = git_buf_detach(&buf);
- return 0;
-}
-
-static int parse_section_header(struct reader *reader, char **section_out)
-{
- char *name, *name_end;
- int name_length, c, pos;
- int result;
- char *line;
- size_t line_len;
-
- line = reader_readline(reader, true);
- if (line == NULL)
- return -1;
-
- /* find the end of the variable's name */
- name_end = strrchr(line, ']');
- if (name_end == NULL) {
- git__free(line);
- set_parse_error(reader, 0, "Missing ']' in section header");
- return -1;
- }
-
- GITERR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1);
- name = git__malloc(line_len);
- GITERR_CHECK_ALLOC(name);
-
- name_length = 0;
- pos = 0;
-
- /* Make sure we were given a section header */
- c = line[pos++];
- assert(c == '[');
-
- c = line[pos++];
-
- do {
- if (git__isspace(c)){
- name[name_length] = '\0';
- result = parse_section_header_ext(reader, line, name, section_out);
- git__free(line);
- git__free(name);
- return result;
- }
-
- if (!config_keychar(c) && c != '.') {
- set_parse_error(reader, pos, "Unexpected character in header");
- goto fail_parse;
- }
-
- name[name_length++] = (char)git__tolower(c);
-
- } while ((c = line[pos++]) != ']');
-
- if (line[pos - 1] != ']') {
- set_parse_error(reader, pos, "Unexpected end of file");
- goto fail_parse;
- }
-
- git__free(line);
-
- name[name_length] = 0;
- *section_out = name;
-
- return 0;
-
-fail_parse:
- git__free(line);
- git__free(name);
- return -1;
-}
-
-static int skip_bom(struct reader *reader)
-{
- git_bom_t bom;
- int bom_offset = git_buf_text_detect_bom(&bom,
- &reader->buffer, reader->read_ptr - reader->buffer.ptr);
-
- if (bom == GIT_BOM_UTF8)
- reader->read_ptr += bom_offset;
-
- /* TODO: reference implementation is pretty stupid with BoM */
-
- return 0;
-}
-
-/*
- (* basic types *)
- digit = "0".."9"
- integer = digit { digit }
- alphabet = "a".."z" + "A" .. "Z"
-
- section_char = alphabet | "." | "-"
- extension_char = (* any character except newline *)
- any_char = (* any character *)
- variable_char = "alphabet" | "-"
-
-
- (* actual grammar *)
- config = { section }
-
- section = header { definition }
-
- header = "[" section [subsection | subsection_ext] "]"
-
- subsection = "." section
- subsection_ext = "\"" extension "\""
-
- section = section_char { section_char }
- extension = extension_char { extension_char }
-
- definition = variable_name ["=" variable_value] "\n"
-
- variable_name = variable_char { variable_char }
- variable_value = string | boolean | integer
-
- string = quoted_string | plain_string
- quoted_string = "\"" plain_string "\""
- plain_string = { any_char }
-
- boolean = boolean_true | boolean_false
- boolean_true = "yes" | "1" | "true" | "on"
- boolean_false = "no" | "0" | "false" | "off"
-*/
-
-static int strip_comments(char *line, int in_quotes)
-{
- int quote_count = in_quotes, backslash_count = 0;
- char *ptr;
-
- for (ptr = line; *ptr; ++ptr) {
- if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
- quote_count++;
-
- if ((ptr[0] == ';' || ptr[0] == '#') &&
- (quote_count % 2) == 0 &&
- (backslash_count % 2) == 0) {
- ptr[0] = '\0';
- break;
- }
-
- if (ptr[0] == '\\')
- backslash_count++;
- else
- backslash_count = 0;
- }
-
- /* skip any space at the end */
- while (ptr > line && git__isspace(ptr[-1])) {
- ptr--;
- }
- ptr[0] = '\0';
-
- return quote_count;
-}
-
-static int included_path(git_buf *out, const char *dir, const char *path)
-{
- /* From the user's home */
- if (path[0] == '~' && path[1] == '/')
- return git_sysdir_find_global_file(out, &path[1]);
-
- return git_path_join_unrooted(out, path, dir, NULL);
-}
-
-static const char *escapes = "ntb\"\\";
-static const char *escaped = "\n\t\b\"\\";
-
-/* Escape the values to write them to the file */
-static char *escape_value(const char *ptr)
-{
- git_buf buf = GIT_BUF_INIT;
- size_t len;
- const char *esc;
-
- assert(ptr);
-
- len = strlen(ptr);
- if (!len)
- return git__calloc(1, sizeof(char));
-
- git_buf_grow(&buf, len);
-
- while (*ptr != '\0') {
- if ((esc = strchr(escaped, *ptr)) != NULL) {
- git_buf_putc(&buf, '\\');
- git_buf_putc(&buf, escapes[esc - escaped]);
- } else {
- git_buf_putc(&buf, *ptr);
- }
- ptr++;
- }
-
- if (git_buf_oom(&buf)) {
- git_buf_free(&buf);
- return NULL;
- }
-
- return git_buf_detach(&buf);
-}
-
-/* '\"' -> '"' etc */
-static int unescape_line(
- char **out, bool *is_multi, const char *ptr, int quote_count)
-{
- char *str, *fixed, *esc;
- size_t ptr_len = strlen(ptr), alloc_len;
-
- *is_multi = false;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, ptr_len, 1) ||
- (str = git__malloc(alloc_len)) == NULL) {
- return -1;
- }
-
- fixed = str;
-
- while (*ptr != '\0') {
- if (*ptr == '"') {
- quote_count++;
- } else if (*ptr != '\\') {
- *fixed++ = *ptr;
- } else {
- /* backslash, check the next char */
- ptr++;
- /* if we're at the end, it's a multiline, so keep the backslash */
- if (*ptr == '\0') {
- *is_multi = true;
- goto done;
- }
- if ((esc = strchr(escapes, *ptr)) != NULL) {
- *fixed++ = escaped[esc - escapes];
- } else {
- git__free(str);
- giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
- return -1;
- }
- }
- ptr++;
- }
-
-done:
- *fixed = '\0';
- *out = str;
-
- return 0;
-}
-
-static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
-{
- char *line = NULL, *proc_line = NULL;
- int quote_count;
- bool multiline;
-
- /* Check that the next line exists */
- line = reader_readline(reader, false);
- if (line == NULL)
- return -1;
-
- /* We've reached the end of the file, there is no continuation.
- * (this is not an error).
- */
- if (line[0] == '\0') {
- git__free(line);
- return 0;
- }
-
- quote_count = strip_comments(line, !!in_quotes);
-
- /* If it was just a comment, pretend it didn't exist */
- if (line[0] == '\0') {
- git__free(line);
- return parse_multiline_variable(reader, value, quote_count);
- /* TODO: unbounded recursion. This **could** be exploitable */
- }
-
- if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
- git__free(line);
- return -1;
- }
- /* add this line to the multiline var */
-
- git_buf_puts(value, proc_line);
- git__free(line);
- git__free(proc_line);
-
- /*
- * If we need to continue reading the next line, let's just
- * keep putting stuff in the buffer
- */
- if (multiline)
- return parse_multiline_variable(reader, value, quote_count);
-
- return 0;
-}
-
-GIT_INLINE(bool) is_namechar(char c)
-{
- return isalnum(c) || c == '-';
-}
-
-static int parse_name(
- char **name, const char **value, struct reader *reader, const char *line)
-{
- const char *name_end = line, *value_start;
-
- *name = NULL;
- *value = NULL;
-
- while (*name_end && is_namechar(*name_end))
- name_end++;
-
- if (line == name_end) {
- set_parse_error(reader, 0, "Invalid configuration key");
- return -1;
- }
-
- value_start = name_end;
-
- while (*value_start && git__isspace(*value_start))
- value_start++;
-
- if (*value_start == '=') {
- *value = value_start + 1;
- } else if (*value_start) {
- set_parse_error(reader, 0, "Invalid configuration key");
- return -1;
- }
-
- if ((*name = git__strndup(line, name_end - line)) == NULL)
- return -1;
-
- return 0;
-}
-
-static int parse_variable(struct reader *reader, char **var_name, char **var_value)
-{
- const char *value_start = NULL;
- char *line;
- int quote_count;
- bool multiline;
-
- line = reader_readline(reader, true);
- if (line == NULL)
- return -1;
-
- quote_count = strip_comments(line, 0);
-
- /* If there is no value, boolean true is assumed */
- *var_value = NULL;
-
- if (parse_name(var_name, &value_start, reader, line) < 0)
- goto on_error;
-
- /*
- * Now, let's try to parse the value
- */
- if (value_start != NULL) {
- while (git__isspace(value_start[0]))
- value_start++;
-
- if (unescape_line(var_value, &multiline, value_start, 0) < 0)
- goto on_error;
-
- if (multiline) {
- git_buf multi_value = GIT_BUF_INIT;
- git_buf_attach(&multi_value, *var_value, 0);
-
- if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 ||
- git_buf_oom(&multi_value)) {
- git_buf_free(&multi_value);
- goto on_error;
- }
-
- *var_value = git_buf_detach(&multi_value);
- }
- }
-
- git__free(line);
- return 0;
-
-on_error:
- git__free(*var_name);
- git__free(line);
- return -1;
-}
-
-static int config_parse(
- struct reader *reader,
- int (*on_section)(struct reader **reader, const char *current_section, const char *line, size_t line_len, void *data),
- int (*on_variable)(struct reader **reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data),
- int (*on_comment)(struct reader **reader, const char *line, size_t line_len, void *data),
- int (*on_eof)(struct reader **reader, const char *current_section, void *data),
- void *data)
-{
- char *current_section = NULL, *var_name, *var_value, *line_start;
- char c;
- size_t line_len;
- int result = 0;
-
- skip_bom(reader);
-
- while (result == 0 && !reader->eof) {
- line_start = reader->read_ptr;
-
- c = reader_peek(reader, SKIP_WHITESPACE);
-
- switch (c) {
- case '\0': /* EOF when peeking, set EOF in the reader to exit the loop */
- reader->eof = 1;
- break;
-
- case '[': /* section header, new section begins */
- git__free(current_section);
- current_section = NULL;
-
- if ((result = parse_section_header(reader, ¤t_section)) == 0 && on_section) {
- line_len = reader->read_ptr - line_start;
- result = on_section(&reader, current_section, line_start, line_len, data);
- }
- break;
-
- case '\n': /* comment or whitespace-only */
- case ';':
- case '#':
- reader_consume_line(reader);
-
- if (on_comment) {
- line_len = reader->read_ptr - line_start;
- result = on_comment(&reader, line_start, line_len, data);
- }
- break;
-
- default: /* assume variable declaration */
- if ((result = parse_variable(reader, &var_name, &var_value)) == 0 && on_variable) {
- line_len = reader->read_ptr - line_start;
- result = on_variable(&reader, current_section, var_name, var_value, line_start, line_len, data);
- }
- break;
- }
- }
-
- if (on_eof)
- result = on_eof(&reader, current_section, data);
-
- git__free(current_section);
- return result;
-}
-
-struct parse_data {
- git_strmap *values;
- diskfile_backend *cfg_file;
- uint32_t reader_idx;
- git_config_level_t level;
- int depth;
-};
-
-static int read_on_variable(
- struct reader **reader,
- const char *current_section,
- char *var_name,
- char *var_value,
- const char *line,
- size_t line_len,
- void *data)
-{
- struct parse_data *parse_data = (struct parse_data *)data;
- git_buf buf = GIT_BUF_INIT;
- cvar_t *var;
- int result = 0;
-
- GIT_UNUSED(line);
- GIT_UNUSED(line_len);
-
- git__strtolower(var_name);
- git_buf_printf(&buf, "%s.%s", current_section, var_name);
- git__free(var_name);
-
- if (git_buf_oom(&buf)) {
- git__free(var_value);
- return -1;
- }
-
- var = git__calloc(1, sizeof(cvar_t));
- GITERR_CHECK_ALLOC(var);
- var->entry = git__calloc(1, sizeof(git_config_entry));
- GITERR_CHECK_ALLOC(var->entry);
-
- var->entry->name = git_buf_detach(&buf);
- var->entry->value = var_value;
- var->entry->level = parse_data->level;
- var->included = !!parse_data->depth;
-
- if ((result = append_entry(parse_data->values, var)) < 0)
- return result;
-
- result = 0;
-
- /* Add or append the new config option */
- if (!git__strcmp(var->entry->name, "include.path")) {
- struct reader *r;
- git_buf path = GIT_BUF_INIT;
- char *dir;
- uint32_t index;
-
- r = git_array_alloc(parse_data->cfg_file->readers);
- /* The reader may have been reallocated */
- *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
- memset(r, 0, sizeof(struct reader));
-
- if ((result = git_path_dirname_r(&path, (*reader)->file_path)) < 0)
- return result;
-
- /* We need to know our index in the array, as the next config_parse call may realloc */
- index = git_array_size(parse_data->cfg_file->readers) - 1;
- dir = git_buf_detach(&path);
- result = included_path(&path, dir, var->entry->value);
- git__free(dir);
-
- if (result < 0)
- return result;
-
- r->file_path = git_buf_detach(&path);
- git_buf_init(&r->buffer, 0);
-
- result = git_futils_readbuffer_updated(
- &r->buffer, r->file_path, &r->checksum, NULL);
-
- if (result == 0) {
- result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1);
- r = git_array_get(parse_data->cfg_file->readers, index);
- *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
- } else if (result == GIT_ENOTFOUND) {
- giterr_clear();
- result = 0;
- }
-
- git_buf_free(&r->buffer);
- }
-
- return result;
-}
-
-static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
-{
- struct parse_data parse_data;
-
- if (depth >= MAX_INCLUDE_DEPTH) {
- giterr_set(GITERR_CONFIG, "Maximum config include depth reached");
- return -1;
- }
-
- /* Initialize the reading position */
- reader->read_ptr = reader->buffer.ptr;
- reader->eof = 0;
-
- /* If the file is empty, there's nothing for us to do */
- if (*reader->read_ptr == '\0')
- return 0;
-
- parse_data.values = values;
- parse_data.cfg_file = cfg_file;
- parse_data.reader_idx = git_array_size(cfg_file->readers) - 1;
- parse_data.level = level;
- parse_data.depth = depth;
-
- return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data);
-}
-
-static int write_section(git_buf *fbuf, const char *key)
-{
- int result;
- const char *dot;
- git_buf buf = GIT_BUF_INIT;
-
- /* All of this just for [section "subsection"] */
- dot = strchr(key, '.');
- git_buf_putc(&buf, '[');
- if (dot == NULL) {
- git_buf_puts(&buf, key);
- } else {
- char *escaped;
- git_buf_put(&buf, key, dot - key);
- escaped = escape_value(dot + 1);
- GITERR_CHECK_ALLOC(escaped);
- git_buf_printf(&buf, " \"%s\"", escaped);
- git__free(escaped);
- }
- git_buf_puts(&buf, "]\n");
-
- if (git_buf_oom(&buf))
- return -1;
-
- result = git_buf_put(fbuf, git_buf_cstr(&buf), buf.size);
- git_buf_free(&buf);
-
- return result;
-}
-
-static const char *quotes_for_value(const char *value)
-{
- const char *ptr;
-
- if (value[0] == ' ' || value[0] == '\0')
- return "\"";
-
- for (ptr = value; *ptr; ++ptr) {
- if (*ptr == ';' || *ptr == '#')
- return "\"";
- }
-
- if (ptr[-1] == ' ')
- return "\"";
-
- return "";
-}
-
-struct write_data {
- git_buf *buf;
- git_buf buffered_comment;
- unsigned int in_section : 1,
- preg_replaced : 1;
- const char *section;
- const char *name;
- const regex_t *preg;
- const char *value;
-};
-
-static int write_line_to(git_buf *buf, const char *line, size_t line_len)
-{
- int result = git_buf_put(buf, line, line_len);
-
- if (!result && line_len && line[line_len-1] != '\n')
- result = git_buf_printf(buf, "\n");
-
- return result;
-}
-
-static int write_line(struct write_data *write_data, const char *line, size_t line_len)
-{
- return write_line_to(write_data->buf, line, line_len);
-}
-
-static int write_value(struct write_data *write_data)
-{
- const char *q;
- int result;
-
- q = quotes_for_value(write_data->value);
- result = git_buf_printf(write_data->buf,
- "\t%s = %s%s%s\n", write_data->name, q, write_data->value, q);
-
- /* If we are updating a single name/value, we're done. Setting `value`
- * to `NULL` will prevent us from trying to write it again later (in
- * `write_on_section`) if we see the same section repeated.
- */
- if (!write_data->preg)
- write_data->value = NULL;
-
- return result;
-}
-
-static int write_on_section(
- struct reader **reader,
- const char *current_section,
- const char *line,
- size_t line_len,
- void *data)
-{
- struct write_data *write_data = (struct write_data *)data;
- int result = 0;
-
- GIT_UNUSED(reader);
-
- /* If we were previously in the correct section (but aren't anymore)
- * and haven't written our value (for a simple name/value set, not
- * a multivar), then append it to the end of the section before writing
- * the new one.
- */
- if (write_data->in_section && !write_data->preg && write_data->value)
- result = write_value(write_data);
-
- write_data->in_section = strcmp(current_section, write_data->section) == 0;
-
- /*
- * If there were comments just before this section, dump them as well.
- */
- if (!result) {
- result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size);
- git_buf_clear(&write_data->buffered_comment);
- }
-
- if (!result)
- result = write_line(write_data, line, line_len);
-
- return result;
-}
-
-static int write_on_variable(
- struct reader **reader,
- const char *current_section,
- char *var_name,
- char *var_value,
- const char *line,
- size_t line_len,
- void *data)
-{
- struct write_data *write_data = (struct write_data *)data;
- bool has_matched = false;
- int error;
-
- GIT_UNUSED(reader);
- GIT_UNUSED(current_section);
-
- /*
- * If there were comments just before this variable, let's dump them as well.
- */
- if ((error = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
- return error;
-
- git_buf_clear(&write_data->buffered_comment);
-
- /* See if we are to update this name/value pair; first examine name */
- if (write_data->in_section &&
- strcasecmp(write_data->name, var_name) == 0)
- has_matched = true;
-
- /* If we have a regex to match the value, see if it matches */
- if (has_matched && write_data->preg != NULL)
- has_matched = (regexec(write_data->preg, var_value, 0, NULL, 0) == 0);
-
- git__free(var_name);
- git__free(var_value);
-
- /* If this isn't the name/value we're looking for, simply dump the
- * existing data back out and continue on.
- */
- if (!has_matched)
- return write_line(write_data, line, line_len);
-
- write_data->preg_replaced = 1;
-
- /* If value is NULL, we are deleting this value; write nothing. */
- if (!write_data->value)
- return 0;
-
- return write_value(write_data);
-}
-
-static int write_on_comment(struct reader **reader, const char *line, size_t line_len, void *data)
-{
- struct write_data *write_data;
-
- GIT_UNUSED(reader);
-
- write_data = (struct write_data *)data;
- return write_line_to(&write_data->buffered_comment, line, line_len);
-}
-
-static int write_on_eof(
- struct reader **reader, const char *current_section, void *data)
-{
- struct write_data *write_data = (struct write_data *)data;
- int result = 0;
-
- GIT_UNUSED(reader);
-
- /*
- * If we've buffered comments when reaching EOF, make sure to dump them.
- */
- if ((result = git_buf_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
- return result;
-
- /* If we are at the EOF and have not written our value (again, for a
- * simple name/value set, not a multivar) then we have never seen the
- * section in question and should create a new section and write the
- * value.
- */
- if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
- /* write the section header unless we're already in it */
- if (!current_section || strcmp(current_section, write_data->section))
- result = write_section(write_data->buf, write_data->section);
-
- if (!result)
- result = write_value(write_data);
- }
-
- return result;
-}
-
-/*
- * This is pretty much the parsing, except we write out anything we don't have
- */
-static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
-{
- int result;
- char *section, *name, *ldot;
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf buf = GIT_BUF_INIT;
- struct reader *reader = git_array_get(cfg->readers, 0);
- struct write_data write_data;
-
- if (cfg->locked) {
- result = git_buf_puts(&reader->buffer, git_buf_cstr(&cfg->locked_content));
- } else {
- /* Lock the file */
- if ((result = git_filebuf_open(
- &file, cfg->file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
- git_buf_free(&reader->buffer);
- return result;
- }
-
- /* We need to read in our own config file */
- result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
- }
-
- /* Initialise the reading position */
- if (result == GIT_ENOTFOUND) {
- reader->read_ptr = NULL;
- reader->eof = 1;
- git_buf_clear(&reader->buffer);
- } else if (result == 0) {
- reader->read_ptr = reader->buffer.ptr;
- reader->eof = 0;
- } else {
- git_filebuf_cleanup(&file);
- return -1; /* OS error when reading the file */
- }
-
- ldot = strrchr(key, '.');
- name = ldot + 1;
- section = git__strndup(key, ldot - key);
-
- write_data.buf = &buf;
- git_buf_init(&write_data.buffered_comment, 0);
- write_data.section = section;
- write_data.in_section = 0;
- write_data.preg_replaced = 0;
- write_data.name = name;
- write_data.preg = preg;
- write_data.value = value;
-
- result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
- git__free(section);
- git_buf_free(&write_data.buffered_comment);
-
- if (result < 0) {
- git_filebuf_cleanup(&file);
- goto done;
- }
-
- if (cfg->locked) {
- size_t len = buf.asize;
- /* Update our copy with the modified contents */
- git_buf_free(&cfg->locked_content);
- git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len);
- } else {
- git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf));
- result = git_filebuf_commit(&file);
- }
-
-done:
- git_buf_free(&buf);
- git_buf_free(&reader->buffer);
- return result;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_config_file_h__
-#define INCLUDE_config_file_h__
-
-#include "git2/config.h"
-
-GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level)
-{
- return cfg->open(cfg, level);
-}
-
-GIT_INLINE(void) git_config_file_free(git_config_backend *cfg)
-{
- if (cfg)
- cfg->free(cfg);
-}
-
-GIT_INLINE(int) git_config_file_get_string(
- git_config_entry **out, git_config_backend *cfg, const char *name)
-{
- return cfg->get(cfg, name, out);
-}
-
-GIT_INLINE(int) git_config_file_set_string(
- git_config_backend *cfg, const char *name, const char *value)
-{
- return cfg->set(cfg, name, value);
-}
-
-GIT_INLINE(int) git_config_file_delete(
- git_config_backend *cfg, const char *name)
-{
- return cfg->del(cfg, name);
-}
-
-GIT_INLINE(int) git_config_file_foreach(
- git_config_backend *cfg,
- int (*fn)(const git_config_entry *entry, void *data),
- void *data)
-{
- return git_config_backend_foreach_match(cfg, NULL, fn, data);
-}
-
-GIT_INLINE(int) git_config_file_foreach_match(
- git_config_backend *cfg,
- const char *regexp,
- int (*fn)(const git_config_entry *entry, void *data),
- void *data)
-{
- return git_config_backend_foreach_match(cfg, regexp, fn, data);
-}
-
-GIT_INLINE(int) git_config_file_lock(git_config_backend *cfg)
-{
- return cfg->lock(cfg);
-}
-
-GIT_INLINE(int) git_config_file_unlock(git_config_backend *cfg, int success)
-{
- return cfg->unlock(cfg, success);
-}
-
-extern int git_config_file_normalize_section(char *start, char *end);
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/attr.h"
-#include "git2/blob.h"
-#include "git2/index.h"
-#include "git2/sys/filter.h"
-
-#include "common.h"
-#include "fileops.h"
-#include "hash.h"
-#include "filter.h"
-#include "buf_text.h"
-#include "repository.h"
-
-struct crlf_attrs {
- int crlf_action;
- int eol;
- int auto_crlf;
- int safe_crlf;
-};
-
-struct crlf_filter {
- git_filter f;
-};
-
-static int check_crlf(const char *value)
-{
- if (GIT_ATTR_TRUE(value))
- return GIT_CRLF_TEXT;
-
- if (GIT_ATTR_FALSE(value))
- return GIT_CRLF_BINARY;
-
- if (GIT_ATTR_UNSPECIFIED(value))
- return GIT_CRLF_GUESS;
-
- if (strcmp(value, "input") == 0)
- return GIT_CRLF_INPUT;
-
- if (strcmp(value, "auto") == 0)
- return GIT_CRLF_AUTO;
-
- return GIT_CRLF_GUESS;
-}
-
-static int check_eol(const char *value)
-{
- if (GIT_ATTR_UNSPECIFIED(value))
- return GIT_EOL_UNSET;
-
- if (strcmp(value, "lf") == 0)
- return GIT_EOL_LF;
-
- if (strcmp(value, "crlf") == 0)
- return GIT_EOL_CRLF;
-
- return GIT_EOL_UNSET;
-}
-
-static int crlf_input_action(struct crlf_attrs *ca)
-{
- if (ca->crlf_action == GIT_CRLF_BINARY)
- return GIT_CRLF_BINARY;
-
- if (ca->eol == GIT_EOL_LF)
- return GIT_CRLF_INPUT;
-
- if (ca->eol == GIT_EOL_CRLF)
- return GIT_CRLF_CRLF;
-
- return ca->crlf_action;
-}
-
-static int has_cr_in_index(const git_filter_source *src)
-{
- git_repository *repo = git_filter_source_repo(src);
- const char *path = git_filter_source_path(src);
- git_index *index;
- const git_index_entry *entry;
- git_blob *blob;
- const void *blobcontent;
- git_off_t blobsize;
- bool found_cr;
-
- if (!path)
- return false;
-
- if (git_repository_index__weakptr(&index, repo) < 0) {
- giterr_clear();
- return false;
- }
-
- if (!(entry = git_index_get_bypath(index, path, 0)) &&
- !(entry = git_index_get_bypath(index, path, 1)))
- return false;
-
- if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
- return true;
-
- if (git_blob_lookup(&blob, repo, &entry->id) < 0)
- return false;
-
- blobcontent = git_blob_rawcontent(blob);
- blobsize = git_blob_rawsize(blob);
- if (!git__is_sizet(blobsize))
- blobsize = (size_t)-1;
-
- found_cr = (blobcontent != NULL &&
- blobsize > 0 &&
- memchr(blobcontent, '\r', (size_t)blobsize) != NULL);
-
- git_blob_free(blob);
- return found_cr;
-}
-
-static int crlf_apply_to_odb(
- struct crlf_attrs *ca,
- git_buf *to,
- const git_buf *from,
- const git_filter_source *src)
-{
- /* Empty file? Nothing to do */
- if (!git_buf_len(from))
- return 0;
-
- /* Heuristics to see if we can skip the conversion.
- * Straight from Core Git.
- */
- if (ca->crlf_action == GIT_CRLF_AUTO || ca->crlf_action == GIT_CRLF_GUESS) {
- git_buf_text_stats stats;
-
- /* Check heuristics for binary vs text - returns true if binary */
- if (git_buf_text_gather_stats(&stats, from, false))
- return GIT_PASSTHROUGH;
-
- /* If there are no CR characters to filter out, then just pass */
- if (!stats.cr)
- return GIT_PASSTHROUGH;
-
- /* If safecrlf is enabled, sanity-check the result. */
- if (stats.cr != stats.crlf || stats.lf != stats.crlf) {
- switch (ca->safe_crlf) {
- case GIT_SAFE_CRLF_FAIL:
- giterr_set(
- GITERR_FILTER, "LF would be replaced by CRLF in '%s'",
- git_filter_source_path(src));
- return -1;
- case GIT_SAFE_CRLF_WARN:
- /* TODO: issue warning when warning API is available */;
- break;
- default:
- break;
- }
- }
-
- /*
- * We're currently not going to even try to convert stuff
- * that has bare CR characters. Does anybody do that crazy
- * stuff?
- */
- if (stats.cr != stats.crlf)
- return GIT_PASSTHROUGH;
-
- if (ca->crlf_action == GIT_CRLF_GUESS) {
- /*
- * If the file in the index has any CR in it, do not convert.
- * This is the new safer autocrlf handling.
- */
- if (has_cr_in_index(src))
- return GIT_PASSTHROUGH;
- }
-
- if (!stats.cr)
- return GIT_PASSTHROUGH;
- }
-
- /* Actually drop the carriage returns */
- return git_buf_text_crlf_to_lf(to, from);
-}
-
-static const char *line_ending(struct crlf_attrs *ca)
-{
- switch (ca->crlf_action) {
- case GIT_CRLF_BINARY:
- case GIT_CRLF_INPUT:
- return "\n";
-
- case GIT_CRLF_CRLF:
- return "\r\n";
-
- case GIT_CRLF_GUESS:
- if (ca->auto_crlf == GIT_AUTO_CRLF_FALSE)
- return "\n";
- break;
-
- case GIT_CRLF_AUTO:
- case GIT_CRLF_TEXT:
- break;
-
- default:
- goto line_ending_error;
- }
-
- if (ca->auto_crlf == GIT_AUTO_CRLF_TRUE)
- return "\r\n";
- else if (ca->auto_crlf == GIT_AUTO_CRLF_INPUT)
- return "\n";
- else if (ca->eol == GIT_EOL_UNSET)
- return GIT_EOL_NATIVE == GIT_EOL_CRLF ? "\r\n" : "\n";
- else if (ca->eol == GIT_EOL_LF)
- return "\n";
- else if (ca->eol == GIT_EOL_CRLF)
- return "\r\n";
-
-line_ending_error:
- giterr_set(GITERR_INVALID, "Invalid input to line ending filter");
- return NULL;
-}
-
-static int crlf_apply_to_workdir(
- struct crlf_attrs *ca, git_buf *to, const git_buf *from)
-{
- git_buf_text_stats stats;
- const char *workdir_ending = NULL;
- bool is_binary;
-
- /* Empty file? Nothing to do. */
- if (git_buf_len(from) == 0)
- return 0;
-
- /* Determine proper line ending */
- workdir_ending = line_ending(ca);
- if (!workdir_ending)
- return -1;
-
- /* only LF->CRLF conversion is supported, do nothing on LF platforms */
- if (strcmp(workdir_ending, "\r\n") != 0)
- return GIT_PASSTHROUGH;
-
- /* If there are no LFs, or all LFs are part of a CRLF, nothing to do */
- is_binary = git_buf_text_gather_stats(&stats, from, false);
-
- if (stats.lf == 0 || stats.lf == stats.crlf)
- return GIT_PASSTHROUGH;
-
- if (ca->crlf_action == GIT_CRLF_AUTO ||
- ca->crlf_action == GIT_CRLF_GUESS) {
-
- /* If we have any existing CR or CRLF line endings, do nothing */
- if (ca->crlf_action == GIT_CRLF_GUESS &&
- stats.cr > 0 && stats.crlf > 0)
- return GIT_PASSTHROUGH;
-
- /* If we have bare CR characters, do nothing */
- if (stats.cr != stats.crlf)
- return GIT_PASSTHROUGH;
-
- /* Don't filter binary files */
- if (is_binary)
- return GIT_PASSTHROUGH;
- }
-
- return git_buf_text_lf_to_crlf(to, from);
-}
-
-static int crlf_check(
- git_filter *self,
- void **payload, /* points to NULL ptr on entry, may be set */
- const git_filter_source *src,
- const char **attr_values)
-{
- int error;
- struct crlf_attrs ca;
-
- GIT_UNUSED(self);
-
- if (!attr_values) {
- ca.crlf_action = GIT_CRLF_GUESS;
- ca.eol = GIT_EOL_UNSET;
- } else {
- ca.crlf_action = check_crlf(attr_values[2]); /* text */
- if (ca.crlf_action == GIT_CRLF_GUESS)
- ca.crlf_action = check_crlf(attr_values[0]); /* clrf */
- ca.eol = check_eol(attr_values[1]); /* eol */
- }
- ca.auto_crlf = GIT_AUTO_CRLF_DEFAULT;
- ca.safe_crlf = GIT_SAFE_CRLF_DEFAULT;
-
- /*
- * Use the core Git logic to see if we should perform CRLF for this file
- * based on its attributes & the value of `core.autocrlf`
- */
- ca.crlf_action = crlf_input_action(&ca);
-
- if (ca.crlf_action == GIT_CRLF_BINARY)
- return GIT_PASSTHROUGH;
-
- if (ca.crlf_action == GIT_CRLF_GUESS ||
- ((ca.crlf_action == GIT_CRLF_AUTO || ca.crlf_action == GIT_CRLF_TEXT) &&
- git_filter_source_mode(src) == GIT_FILTER_SMUDGE)) {
-
- error = git_repository__cvar(
- &ca.auto_crlf, git_filter_source_repo(src), GIT_CVAR_AUTO_CRLF);
- if (error < 0)
- return error;
-
- if (ca.crlf_action == GIT_CRLF_GUESS &&
- ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
- return GIT_PASSTHROUGH;
-
- if (ca.auto_crlf == GIT_AUTO_CRLF_INPUT &&
- git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
- return GIT_PASSTHROUGH;
- }
-
- if (git_filter_source_mode(src) == GIT_FILTER_CLEAN) {
- error = git_repository__cvar(
- &ca.safe_crlf, git_filter_source_repo(src), GIT_CVAR_SAFE_CRLF);
- if (error < 0)
- return error;
-
- /* downgrade FAIL to WARN if ALLOW_UNSAFE option is used */
- if ((git_filter_source_flags(src) & GIT_FILTER_ALLOW_UNSAFE) &&
- ca.safe_crlf == GIT_SAFE_CRLF_FAIL)
- ca.safe_crlf = GIT_SAFE_CRLF_WARN;
- }
-
- *payload = git__malloc(sizeof(ca));
- GITERR_CHECK_ALLOC(*payload);
- memcpy(*payload, &ca, sizeof(ca));
-
- return 0;
-}
-
-static int crlf_apply(
- git_filter *self,
- void **payload, /* may be read and/or set */
- git_buf *to,
- const git_buf *from,
- const git_filter_source *src)
-{
- /* initialize payload in case `check` was bypassed */
- if (!*payload) {
- int error = crlf_check(self, payload, src, NULL);
- if (error < 0)
- return error;
- }
-
- if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
- return crlf_apply_to_workdir(*payload, to, from);
- else
- return crlf_apply_to_odb(*payload, to, from, src);
-}
-
-static void crlf_cleanup(
- git_filter *self,
- void *payload)
-{
- GIT_UNUSED(self);
- git__free(payload);
-}
-
-git_filter *git_crlf_filter_new(void)
-{
- struct crlf_filter *f = git__calloc(1, sizeof(struct crlf_filter));
- if (f == NULL)
- return NULL;
-
- f->f.version = GIT_FILTER_VERSION;
- f->f.attributes = "crlf eol text";
- f->f.initialize = NULL;
- f->f.shutdown = git_filter_free;
- f->f.check = crlf_check;
- f->f.apply = crlf_apply;
- f->f.cleanup = crlf_cleanup;
-
- return (git_filter *)f;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifdef GIT_CURL
-
-#include <curl/curl.h>
-
-#include "stream.h"
-#include "git2/transport.h"
-#include "buffer.h"
-#include "vector.h"
-#include "proxy.h"
-
-/* This is for backwards compatibility with curl<7.45.0. */
-#ifndef CURLINFO_ACTIVESOCKET
-# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET
-# define GIT_CURL_BADSOCKET -1
-# define git_activesocket_t long
-#else
-# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD
-# define git_activesocket_t curl_socket_t
-#endif
-
-typedef struct {
- git_stream parent;
- CURL *handle;
- curl_socket_t socket;
- char curl_error[CURL_ERROR_SIZE + 1];
- git_cert_x509 cert_info;
- git_strarray cert_info_strings;
- git_proxy_options proxy;
- git_cred *proxy_cred;
-} curl_stream;
-
-static int seterr_curl(curl_stream *s)
-{
- giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error);
- return -1;
-}
-
-GIT_INLINE(int) error_no_credentials(void)
-{
- giterr_set(GITERR_NET, "proxy authentication required, but no callback provided");
- return GIT_EAUTH;
-}
-
-static int apply_proxy_creds(curl_stream *s)
-{
- CURLcode res;
- git_cred_userpass_plaintext *userpass;
-
- if (!s->proxy_cred)
- return GIT_ENOTFOUND;
-
- userpass = (git_cred_userpass_plaintext *) s->proxy_cred;
- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK)
- return seterr_curl(s);
- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK)
- return seterr_curl(s);
-
- return 0;
-}
-
-static int ask_and_apply_proxy_creds(curl_stream *s)
-{
- int error;
- git_proxy_options *opts = &s->proxy;
-
- if (!opts->credentials)
- return error_no_credentials();
-
- /* TODO: see if PROXYAUTH_AVAIL helps us here */
- git_cred_free(s->proxy_cred);
- s->proxy_cred = NULL;
- giterr_clear();
- error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload);
- if (error == GIT_PASSTHROUGH)
- return error_no_credentials();
- if (error < 0) {
- if (!giterr_last())
- giterr_set(GITERR_NET, "proxy authentication was aborted by the user");
- return error;
- }
-
- if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
- giterr_set(GITERR_NET, "credentials callback returned invalid credential type");
- return -1;
- }
-
- return apply_proxy_creds(s);
-}
-
-static int curls_connect(git_stream *stream)
-{
- curl_stream *s = (curl_stream *) stream;
- git_activesocket_t sockextr;
- long connect_last = 0;
- int failed_cert = 0, error;
- bool retry_connect;
- CURLcode res;
-
- /* Apply any credentials we've already established */
- error = apply_proxy_creds(s);
- if (error < 0 && error != GIT_ENOTFOUND)
- return seterr_curl(s);
-
- do {
- retry_connect = 0;
- res = curl_easy_perform(s->handle);
-
- curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last);
-
- /* HTTP 407 Proxy Authentication Required */
- if (connect_last == 407) {
- if ((error = ask_and_apply_proxy_creds(s)) < 0)
- return error;
-
- retry_connect = true;
- }
- } while (retry_connect);
-
- if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
- return seterr_curl(s);
- if (res == CURLE_PEER_FAILED_VERIFICATION)
- failed_cert = 1;
-
- if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) {
- return seterr_curl(s);
- }
-
- if (sockextr == GIT_CURL_BADSOCKET) {
- giterr_set(GITERR_NET, "curl socket is no longer valid");
- return -1;
- }
-
- s->socket = sockextr;
-
- if (s->parent.encrypted && failed_cert)
- return GIT_ECERTIFICATE;
-
- return 0;
-}
-
-static int curls_certificate(git_cert **out, git_stream *stream)
-{
- int error;
- CURLcode res;
- struct curl_slist *slist;
- struct curl_certinfo *certinfo;
- git_vector strings = GIT_VECTOR_INIT;
- curl_stream *s = (curl_stream *) stream;
-
- if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK)
- return seterr_curl(s);
-
- /* No information is available, can happen with SecureTransport */
- if (certinfo->num_of_certs == 0) {
- s->cert_info.parent.cert_type = GIT_CERT_NONE;
- s->cert_info.data = NULL;
- s->cert_info.len = 0;
- return 0;
- }
-
- if ((error = git_vector_init(&strings, 8, NULL)) < 0)
- return error;
-
- for (slist = certinfo->certinfo[0]; slist; slist = slist->next) {
- char *str = git__strdup(slist->data);
- GITERR_CHECK_ALLOC(str);
- git_vector_insert(&strings, str);
- }
-
- /* Copy the contents of the vector into a strarray so we can expose them */
- s->cert_info_strings.strings = (char **) strings.contents;
- s->cert_info_strings.count = strings.length;
-
- s->cert_info.parent.cert_type = GIT_CERT_STRARRAY;
- s->cert_info.data = &s->cert_info_strings;
- s->cert_info.len = strings.length;
-
- *out = &s->cert_info.parent;
-
- return 0;
-}
-
-static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
-{
- int error;
- CURLcode res;
- curl_stream *s = (curl_stream *) stream;
-
- if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0)
- return error;
-
- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK)
- return seterr_curl(s);
-
- if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK)
- return seterr_curl(s);
-
- return 0;
-}
-
-static int wait_for(curl_socket_t fd, bool reading)
-{
- int ret;
- fd_set infd, outfd, errfd;
-
- FD_ZERO(&infd);
- FD_ZERO(&outfd);
- FD_ZERO(&errfd);
-
- assert(fd >= 0);
- FD_SET(fd, &errfd);
- if (reading)
- FD_SET(fd, &infd);
- else
- FD_SET(fd, &outfd);
-
- if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) {
- giterr_set(GITERR_OS, "error in select");
- return -1;
- }
-
- return 0;
-}
-
-static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags)
-{
- int error;
- size_t off = 0, sent;
- CURLcode res;
- curl_stream *s = (curl_stream *) stream;
-
- GIT_UNUSED(flags);
-
- do {
- if ((error = wait_for(s->socket, false)) < 0)
- return error;
-
- res = curl_easy_send(s->handle, data + off, len - off, &sent);
- if (res == CURLE_OK)
- off += sent;
- } while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len);
-
- if (res != CURLE_OK)
- return seterr_curl(s);
-
- return len;
-}
-
-static ssize_t curls_read(git_stream *stream, void *data, size_t len)
-{
- int error;
- size_t read;
- CURLcode res;
- curl_stream *s = (curl_stream *) stream;
-
- do {
- if ((error = wait_for(s->socket, true)) < 0)
- return error;
-
- res = curl_easy_recv(s->handle, data, len, &read);
- } while (res == CURLE_AGAIN);
-
- if (res != CURLE_OK)
- return seterr_curl(s);
-
- return read;
-}
-
-static int curls_close(git_stream *stream)
-{
- curl_stream *s = (curl_stream *) stream;
-
- if (!s->handle)
- return 0;
-
- curl_easy_cleanup(s->handle);
- s->handle = NULL;
- s->socket = 0;
-
- return 0;
-}
-
-static void curls_free(git_stream *stream)
-{
- curl_stream *s = (curl_stream *) stream;
-
- curls_close(stream);
- git_strarray_free(&s->cert_info_strings);
- git__free(s);
-}
-
-int git_curl_stream_new(git_stream **out, const char *host, const char *port)
-{
- curl_stream *st;
- CURL *handle;
- int iport = 0, error;
-
- st = git__calloc(1, sizeof(curl_stream));
- GITERR_CHECK_ALLOC(st);
-
- handle = curl_easy_init();
- if (handle == NULL) {
- giterr_set(GITERR_NET, "failed to create curl handle");
- git__free(st);
- return -1;
- }
-
- if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) {
- git__free(st);
- return error;
- }
-
- curl_easy_setopt(handle, CURLOPT_URL, host);
- curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
- curl_easy_setopt(handle, CURLOPT_PORT, iport);
- curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1);
- curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1);
- curl_easy_setopt(handle, CURLOPT_CERTINFO, 1);
- curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1);
- curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
-
- /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */
-
- st->parent.version = GIT_STREAM_VERSION;
- st->parent.encrypted = 0; /* we don't encrypt ourselves */
- st->parent.proxy_support = 1;
- st->parent.connect = curls_connect;
- st->parent.certificate = curls_certificate;
- st->parent.set_proxy = curls_set_proxy;
- st->parent.read = curls_read;
- st->parent.write = curls_write;
- st->parent.close = curls_close;
- st->parent.free = curls_free;
- st->handle = handle;
-
- *out = (git_stream *) st;
- return 0;
-}
-
-#else
-
-#include "stream.h"
-
-int git_curl_stream_new(git_stream **out, const char *host, const char *port)
-{
- GIT_UNUSED(out);
- GIT_UNUSED(host);
- GIT_UNUSED(port);
-
- giterr_set(GITERR_NET, "curl is not supported in this version");
- return -1;
-}
-
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_curl_stream_h__
-#define INCLUDE_curl_stream_h__
-
-#include "git2/sys/stream.h"
-
-extern int git_curl_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
+++ /dev/null
-/*
- * GIT - The information manager from hell
- *
- * Copyright (C) Linus Torvalds, 2005
- */
-
-#include "common.h"
-
-#ifndef GIT_WIN32
-#include <sys/time.h>
-#endif
-
-#include "util.h"
-#include "cache.h"
-#include "posix.h"
-
-#include <ctype.h>
-#include <time.h>
-
-typedef enum {
- DATE_NORMAL = 0,
- DATE_RELATIVE,
- DATE_SHORT,
- DATE_LOCAL,
- DATE_ISO8601,
- DATE_RFC2822,
- DATE_RAW
-} date_mode;
-
-/*
- * This is like mktime, but without normalization of tm_wday and tm_yday.
- */
-static git_time_t tm_to_time_t(const struct tm *tm)
-{
- static const int mdays[] = {
- 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
- };
- int year = tm->tm_year - 70;
- int month = tm->tm_mon;
- int day = tm->tm_mday;
-
- if (year < 0 || year > 129) /* algo only works for 1970-2099 */
- return -1;
- if (month < 0 || month > 11) /* array bounds */
- return -1;
- if (month < 2 || (year + 2) % 4)
- day--;
- if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
- return -1;
- return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
- tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
-}
-
-static const char *month_names[] = {
- "January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"
-};
-
-static const char *weekday_names[] = {
- "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
-};
-
-
-
-/*
- * Check these. And note how it doesn't do the summer-time conversion.
- *
- * In my world, it's always summer, and things are probably a bit off
- * in other ways too.
- */
-static const struct {
- const char *name;
- int offset;
- int dst;
-} timezone_names[] = {
- { "IDLW", -12, 0, }, /* International Date Line West */
- { "NT", -11, 0, }, /* Nome */
- { "CAT", -10, 0, }, /* Central Alaska */
- { "HST", -10, 0, }, /* Hawaii Standard */
- { "HDT", -10, 1, }, /* Hawaii Daylight */
- { "YST", -9, 0, }, /* Yukon Standard */
- { "YDT", -9, 1, }, /* Yukon Daylight */
- { "PST", -8, 0, }, /* Pacific Standard */
- { "PDT", -8, 1, }, /* Pacific Daylight */
- { "MST", -7, 0, }, /* Mountain Standard */
- { "MDT", -7, 1, }, /* Mountain Daylight */
- { "CST", -6, 0, }, /* Central Standard */
- { "CDT", -6, 1, }, /* Central Daylight */
- { "EST", -5, 0, }, /* Eastern Standard */
- { "EDT", -5, 1, }, /* Eastern Daylight */
- { "AST", -3, 0, }, /* Atlantic Standard */
- { "ADT", -3, 1, }, /* Atlantic Daylight */
- { "WAT", -1, 0, }, /* West Africa */
-
- { "GMT", 0, 0, }, /* Greenwich Mean */
- { "UTC", 0, 0, }, /* Universal (Coordinated) */
- { "Z", 0, 0, }, /* Zulu, alias for UTC */
-
- { "WET", 0, 0, }, /* Western European */
- { "BST", 0, 1, }, /* British Summer */
- { "CET", +1, 0, }, /* Central European */
- { "MET", +1, 0, }, /* Middle European */
- { "MEWT", +1, 0, }, /* Middle European Winter */
- { "MEST", +1, 1, }, /* Middle European Summer */
- { "CEST", +1, 1, }, /* Central European Summer */
- { "MESZ", +1, 1, }, /* Middle European Summer */
- { "FWT", +1, 0, }, /* French Winter */
- { "FST", +1, 1, }, /* French Summer */
- { "EET", +2, 0, }, /* Eastern Europe */
- { "EEST", +2, 1, }, /* Eastern European Daylight */
- { "WAST", +7, 0, }, /* West Australian Standard */
- { "WADT", +7, 1, }, /* West Australian Daylight */
- { "CCT", +8, 0, }, /* China Coast */
- { "JST", +9, 0, }, /* Japan Standard */
- { "EAST", +10, 0, }, /* Eastern Australian Standard */
- { "EADT", +10, 1, }, /* Eastern Australian Daylight */
- { "GST", +10, 0, }, /* Guam Standard */
- { "NZT", +12, 0, }, /* New Zealand */
- { "NZST", +12, 0, }, /* New Zealand Standard */
- { "NZDT", +12, 1, }, /* New Zealand Daylight */
- { "IDLE", +12, 0, }, /* International Date Line East */
-};
-
-static size_t match_string(const char *date, const char *str)
-{
- size_t i = 0;
-
- for (i = 0; *date; date++, str++, i++) {
- if (*date == *str)
- continue;
- if (toupper(*date) == toupper(*str))
- continue;
- if (!isalnum(*date))
- break;
- return 0;
- }
- return i;
-}
-
-static int skip_alpha(const char *date)
-{
- int i = 0;
- do {
- i++;
- } while (isalpha(date[i]));
- return i;
-}
-
-/*
-* Parse month, weekday, or timezone name
-*/
-static size_t match_alpha(const char *date, struct tm *tm, int *offset)
-{
- unsigned int i;
-
- for (i = 0; i < 12; i++) {
- size_t match = match_string(date, month_names[i]);
- if (match >= 3) {
- tm->tm_mon = i;
- return match;
- }
- }
-
- for (i = 0; i < 7; i++) {
- size_t match = match_string(date, weekday_names[i]);
- if (match >= 3) {
- tm->tm_wday = i;
- return match;
- }
- }
-
- for (i = 0; i < ARRAY_SIZE(timezone_names); i++) {
- size_t match = match_string(date, timezone_names[i].name);
- if (match >= 3 || match == strlen(timezone_names[i].name)) {
- int off = timezone_names[i].offset;
-
- /* This is bogus, but we like summer */
- off += timezone_names[i].dst;
-
- /* Only use the tz name offset if we don't have anything better */
- if (*offset == -1)
- *offset = 60*off;
-
- return match;
- }
- }
-
- if (match_string(date, "PM") == 2) {
- tm->tm_hour = (tm->tm_hour % 12) + 12;
- return 2;
- }
-
- if (match_string(date, "AM") == 2) {
- tm->tm_hour = (tm->tm_hour % 12) + 0;
- return 2;
- }
-
- /* BAD */
- return skip_alpha(date);
-}
-
-static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm)
-{
- if (month > 0 && month < 13 && day > 0 && day < 32) {
- struct tm check = *tm;
- struct tm *r = (now_tm ? &check : tm);
- time_t specified;
-
- r->tm_mon = month - 1;
- r->tm_mday = day;
- if (year == -1) {
- if (!now_tm)
- return 1;
- r->tm_year = now_tm->tm_year;
- }
- else if (year >= 1970 && year < 2100)
- r->tm_year = year - 1900;
- else if (year > 70 && year < 100)
- r->tm_year = year;
- else if (year < 38)
- r->tm_year = year + 100;
- else
- return 0;
- if (!now_tm)
- return 1;
-
- specified = tm_to_time_t(r);
-
- /* Be it commit time or author time, it does not make
- * sense to specify timestamp way into the future. Make
- * sure it is not later than ten days from now...
- */
- if (now + 10*24*3600 < specified)
- return 0;
- tm->tm_mon = r->tm_mon;
- tm->tm_mday = r->tm_mday;
- if (year != -1)
- tm->tm_year = r->tm_year;
- return 1;
- }
- return 0;
-}
-
-static size_t match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm)
-{
- time_t now;
- struct tm now_tm;
- struct tm *refuse_future;
- long num2, num3;
-
- num2 = strtol(end+1, &end, 10);
- num3 = -1;
- if (*end == c && isdigit(end[1]))
- num3 = strtol(end+1, &end, 10);
-
- /* Time? Date? */
- switch (c) {
- case ':':
- if (num3 < 0)
- num3 = 0;
- if (num < 25 && num2 >= 0 && num2 < 60 && num3 >= 0 && num3 <= 60) {
- tm->tm_hour = num;
- tm->tm_min = num2;
- tm->tm_sec = num3;
- break;
- }
- return 0;
-
- case '-':
- case '/':
- case '.':
- now = time(NULL);
- refuse_future = NULL;
- if (p_gmtime_r(&now, &now_tm))
- refuse_future = &now_tm;
-
- if (num > 70) {
- /* yyyy-mm-dd? */
- if (is_date(num, num2, num3, refuse_future, now, tm))
- break;
- /* yyyy-dd-mm? */
- if (is_date(num, num3, num2, refuse_future, now, tm))
- break;
- }
- /* Our eastern European friends say dd.mm.yy[yy]
- * is the norm there, so giving precedence to
- * mm/dd/yy[yy] form only when separator is not '.'
- */
- if (c != '.' &&
- is_date(num3, num, num2, refuse_future, now, tm))
- break;
- /* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */
- if (is_date(num3, num2, num, refuse_future, now, tm))
- break;
- /* Funny European mm.dd.yy */
- if (c == '.' &&
- is_date(num3, num, num2, refuse_future, now, tm))
- break;
- return 0;
- }
- return end - date;
-}
-
-/*
- * Have we filled in any part of the time/date yet?
- * We just do a binary 'and' to see if the sign bit
- * is set in all the values.
- */
-static int nodate(struct tm *tm)
-{
- return (tm->tm_year &
- tm->tm_mon &
- tm->tm_mday &
- tm->tm_hour &
- tm->tm_min &
- tm->tm_sec) < 0;
-}
-
-/*
- * We've seen a digit. Time? Year? Date?
- */
-static size_t match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt)
-{
- size_t n;
- char *end;
- unsigned long num;
-
- num = strtoul(date, &end, 10);
-
- /*
- * Seconds since 1970? We trigger on that for any numbers with
- * more than 8 digits. This is because we don't want to rule out
- * numbers like 20070606 as a YYYYMMDD date.
- */
- if (num >= 100000000 && nodate(tm)) {
- time_t time = num;
- if (p_gmtime_r(&time, tm)) {
- *tm_gmt = 1;
- return end - date;
- }
- }
-
- /*
- * Check for special formats: num[-.:/]num[same]num
- */
- switch (*end) {
- case ':':
- case '.':
- case '/':
- case '-':
- if (isdigit(end[1])) {
- size_t match = match_multi_number(num, *end, date, end, tm);
- if (match)
- return match;
- }
- }
-
- /*
- * None of the special formats? Try to guess what
- * the number meant. We use the number of digits
- * to make a more educated guess..
- */
- n = 0;
- do {
- n++;
- } while (isdigit(date[n]));
-
- /* Four-digit year or a timezone? */
- if (n == 4) {
- if (num <= 1400 && *offset == -1) {
- unsigned int minutes = num % 100;
- unsigned int hours = num / 100;
- *offset = hours*60 + minutes;
- } else if (num > 1900 && num < 2100)
- tm->tm_year = num - 1900;
- return n;
- }
-
- /*
- * Ignore lots of numerals. We took care of 4-digit years above.
- * Days or months must be one or two digits.
- */
- if (n > 2)
- return n;
-
- /*
- * NOTE! We will give precedence to day-of-month over month or
- * year numbers in the 1-12 range. So 05 is always "mday 5",
- * unless we already have a mday..
- *
- * IOW, 01 Apr 05 parses as "April 1st, 2005".
- */
- if (num > 0 && num < 32 && tm->tm_mday < 0) {
- tm->tm_mday = num;
- return n;
- }
-
- /* Two-digit year? */
- if (n == 2 && tm->tm_year < 0) {
- if (num < 10 && tm->tm_mday >= 0) {
- tm->tm_year = num + 100;
- return n;
- }
- if (num >= 70) {
- tm->tm_year = num;
- return n;
- }
- }
-
- if (num > 0 && num < 13 && tm->tm_mon < 0)
- tm->tm_mon = num-1;
-
- return n;
-}
-
-static size_t match_tz(const char *date, int *offp)
-{
- char *end;
- int hour = strtoul(date + 1, &end, 10);
- size_t n = end - (date + 1);
- int min = 0;
-
- if (n == 4) {
- /* hhmm */
- min = hour % 100;
- hour = hour / 100;
- } else if (n != 2) {
- min = 99; /* random stuff */
- } else if (*end == ':') {
- /* hh:mm? */
- min = strtoul(end + 1, &end, 10);
- if (end - (date + 1) != 5)
- min = 99; /* random stuff */
- } /* otherwise we parsed "hh" */
-
- /*
- * Don't accept any random stuff. Even though some places have
- * offset larger than 12 hours (e.g. Pacific/Kiritimati is at
- * UTC+14), there is something wrong if hour part is much
- * larger than that. We might also want to check that the
- * minutes are divisible by 15 or something too. (Offset of
- * Kathmandu, Nepal is UTC+5:45)
- */
- if (min < 60 && hour < 24) {
- int offset = hour * 60 + min;
- if (*date == '-')
- offset = -offset;
- *offp = offset;
- }
- return end - date;
-}
-
-/*
- * Parse a string like "0 +0000" as ancient timestamp near epoch, but
- * only when it appears not as part of any other string.
- */
-static int match_object_header_date(const char *date, git_time_t *timestamp, int *offset)
-{
- char *end;
- unsigned long stamp;
- int ofs;
-
- if (*date < '0' || '9' <= *date)
- return -1;
- stamp = strtoul(date, &end, 10);
- if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
- return -1;
- date = end + 2;
- ofs = strtol(date, &end, 10);
- if ((*end != '\0' && (*end != '\n')) || end != date + 4)
- return -1;
- ofs = (ofs / 100) * 60 + (ofs % 100);
- if (date[-1] == '-')
- ofs = -ofs;
- *timestamp = stamp;
- *offset = ofs;
- return 0;
-}
-
-/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
- (i.e. English) day/month names, and it doesn't work correctly with %z. */
-static int parse_date_basic(const char *date, git_time_t *timestamp, int *offset)
-{
- struct tm tm;
- int tm_gmt;
- git_time_t dummy_timestamp;
- int dummy_offset;
-
- if (!timestamp)
- timestamp = &dummy_timestamp;
- if (!offset)
- offset = &dummy_offset;
-
- memset(&tm, 0, sizeof(tm));
- tm.tm_year = -1;
- tm.tm_mon = -1;
- tm.tm_mday = -1;
- tm.tm_isdst = -1;
- tm.tm_hour = -1;
- tm.tm_min = -1;
- tm.tm_sec = -1;
- *offset = -1;
- tm_gmt = 0;
-
- if (*date == '@' &&
- !match_object_header_date(date + 1, timestamp, offset))
- return 0; /* success */
- for (;;) {
- size_t match = 0;
- unsigned char c = *date;
-
- /* Stop at end of string or newline */
- if (!c || c == '\n')
- break;
-
- if (isalpha(c))
- match = match_alpha(date, &tm, offset);
- else if (isdigit(c))
- match = match_digit(date, &tm, offset, &tm_gmt);
- else if ((c == '-' || c == '+') && isdigit(date[1]))
- match = match_tz(date, offset);
-
- if (!match) {
- /* BAD */
- match = 1;
- }
-
- date += match;
- }
-
- /* mktime uses local timezone */
- *timestamp = tm_to_time_t(&tm);
- if (*offset == -1)
- *offset = (int)((time_t)*timestamp - mktime(&tm)) / 60;
-
- if (*timestamp == (git_time_t)-1)
- return -1;
-
- if (!tm_gmt)
- *timestamp -= *offset * 60;
- return 0; /* success */
-}
-
-
-/*
- * Relative time update (eg "2 days ago"). If we haven't set the time
- * yet, we need to set it from current time.
- */
-static git_time_t update_tm(struct tm *tm, struct tm *now, unsigned long sec)
-{
- time_t n;
-
- if (tm->tm_mday < 0)
- tm->tm_mday = now->tm_mday;
- if (tm->tm_mon < 0)
- tm->tm_mon = now->tm_mon;
- if (tm->tm_year < 0) {
- tm->tm_year = now->tm_year;
- if (tm->tm_mon > now->tm_mon)
- tm->tm_year--;
- }
-
- n = mktime(tm) - sec;
- p_localtime_r(&n, tm);
- return n;
-}
-
-static void date_now(struct tm *tm, struct tm *now, int *num)
-{
- GIT_UNUSED(num);
- update_tm(tm, now, 0);
-}
-
-static void date_yesterday(struct tm *tm, struct tm *now, int *num)
-{
- GIT_UNUSED(num);
- update_tm(tm, now, 24*60*60);
-}
-
-static void date_time(struct tm *tm, struct tm *now, int hour)
-{
- if (tm->tm_hour < hour)
- date_yesterday(tm, now, NULL);
- tm->tm_hour = hour;
- tm->tm_min = 0;
- tm->tm_sec = 0;
-}
-
-static void date_midnight(struct tm *tm, struct tm *now, int *num)
-{
- GIT_UNUSED(num);
- date_time(tm, now, 0);
-}
-
-static void date_noon(struct tm *tm, struct tm *now, int *num)
-{
- GIT_UNUSED(num);
- date_time(tm, now, 12);
-}
-
-static void date_tea(struct tm *tm, struct tm *now, int *num)
-{
- GIT_UNUSED(num);
- date_time(tm, now, 17);
-}
-
-static void date_pm(struct tm *tm, struct tm *now, int *num)
-{
- int hour, n = *num;
- *num = 0;
- GIT_UNUSED(now);
-
- hour = tm->tm_hour;
- if (n) {
- hour = n;
- tm->tm_min = 0;
- tm->tm_sec = 0;
- }
- tm->tm_hour = (hour % 12) + 12;
-}
-
-static void date_am(struct tm *tm, struct tm *now, int *num)
-{
- int hour, n = *num;
- *num = 0;
- GIT_UNUSED(now);
-
- hour = tm->tm_hour;
- if (n) {
- hour = n;
- tm->tm_min = 0;
- tm->tm_sec = 0;
- }
- tm->tm_hour = (hour % 12);
-}
-
-static void date_never(struct tm *tm, struct tm *now, int *num)
-{
- time_t n = 0;
- GIT_UNUSED(now);
- GIT_UNUSED(num);
- p_localtime_r(&n, tm);
-}
-
-static const struct special {
- const char *name;
- void (*fn)(struct tm *, struct tm *, int *);
-} special[] = {
- { "yesterday", date_yesterday },
- { "noon", date_noon },
- { "midnight", date_midnight },
- { "tea", date_tea },
- { "PM", date_pm },
- { "AM", date_am },
- { "never", date_never },
- { "now", date_now },
- { NULL }
-};
-
-static const char *number_name[] = {
- "zero", "one", "two", "three", "four",
- "five", "six", "seven", "eight", "nine", "ten",
-};
-
-static const struct typelen {
- const char *type;
- int length;
-} typelen[] = {
- { "seconds", 1 },
- { "minutes", 60 },
- { "hours", 60*60 },
- { "days", 24*60*60 },
- { "weeks", 7*24*60*60 },
- { NULL }
-};
-
-static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched)
-{
- const struct typelen *tl;
- const struct special *s;
- const char *end = date;
- int i;
-
- while (isalpha(*++end))
- /* scan to non-alpha */;
-
- for (i = 0; i < 12; i++) {
- size_t match = match_string(date, month_names[i]);
- if (match >= 3) {
- tm->tm_mon = i;
- *touched = 1;
- return end;
- }
- }
-
- for (s = special; s->name; s++) {
- size_t len = strlen(s->name);
- if (match_string(date, s->name) == len) {
- s->fn(tm, now, num);
- *touched = 1;
- return end;
- }
- }
-
- if (!*num) {
- for (i = 1; i < 11; i++) {
- size_t len = strlen(number_name[i]);
- if (match_string(date, number_name[i]) == len) {
- *num = i;
- *touched = 1;
- return end;
- }
- }
- if (match_string(date, "last") == 4) {
- *num = 1;
- *touched = 1;
- }
- return end;
- }
-
- tl = typelen;
- while (tl->type) {
- size_t len = strlen(tl->type);
- if (match_string(date, tl->type) >= len-1) {
- update_tm(tm, now, tl->length * *num);
- *num = 0;
- *touched = 1;
- return end;
- }
- tl++;
- }
-
- for (i = 0; i < 7; i++) {
- size_t match = match_string(date, weekday_names[i]);
- if (match >= 3) {
- int diff, n = *num -1;
- *num = 0;
-
- diff = tm->tm_wday - i;
- if (diff <= 0)
- n++;
- diff += 7*n;
-
- update_tm(tm, now, diff * 24 * 60 * 60);
- *touched = 1;
- return end;
- }
- }
-
- if (match_string(date, "months") >= 5) {
- int n;
- update_tm(tm, now, 0); /* fill in date fields if needed */
- n = tm->tm_mon - *num;
- *num = 0;
- while (n < 0) {
- n += 12;
- tm->tm_year--;
- }
- tm->tm_mon = n;
- *touched = 1;
- return end;
- }
-
- if (match_string(date, "years") >= 4) {
- update_tm(tm, now, 0); /* fill in date fields if needed */
- tm->tm_year -= *num;
- *num = 0;
- *touched = 1;
- return end;
- }
-
- return end;
-}
-
-static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
-{
- char *end;
- unsigned long number = strtoul(date, &end, 10);
-
- switch (*end) {
- case ':':
- case '.':
- case '/':
- case '-':
- if (isdigit(end[1])) {
- size_t match = match_multi_number(number, *end, date, end, tm);
- if (match)
- return date + match;
- }
- }
-
- /* Accept zero-padding only for small numbers ("Dec 02", never "Dec 0002") */
- if (date[0] != '0' || end - date <= 2)
- *num = number;
- return end;
-}
-
-/*
- * Do we have a pending number at the end, or when
- * we see a new one? Let's assume it's a month day,
- * as in "Dec 6, 1992"
- */
-static void pending_number(struct tm *tm, int *num)
-{
- int number = *num;
-
- if (number) {
- *num = 0;
- if (tm->tm_mday < 0 && number < 32)
- tm->tm_mday = number;
- else if (tm->tm_mon < 0 && number < 13)
- tm->tm_mon = number-1;
- else if (tm->tm_year < 0) {
- if (number > 1969 && number < 2100)
- tm->tm_year = number - 1900;
- else if (number > 69 && number < 100)
- tm->tm_year = number;
- else if (number < 38)
- tm->tm_year = 100 + number;
- /* We mess up for number = 00 ? */
- }
- }
-}
-
-static git_time_t approxidate_str(const char *date,
- time_t time_sec,
- int *error_ret)
-{
- int number = 0;
- int touched = 0;
- struct tm tm = {0}, now;
-
- p_localtime_r(&time_sec, &tm);
- now = tm;
-
- tm.tm_year = -1;
- tm.tm_mon = -1;
- tm.tm_mday = -1;
-
- for (;;) {
- unsigned char c = *date;
- if (!c)
- break;
- date++;
- if (isdigit(c)) {
- pending_number(&tm, &number);
- date = approxidate_digit(date-1, &tm, &number);
- touched = 1;
- continue;
- }
- if (isalpha(c))
- date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
- }
- pending_number(&tm, &number);
- if (!touched)
- *error_ret = 1;
- return update_tm(&tm, &now, 0);
-}
-
-int git__date_parse(git_time_t *out, const char *date)
-{
- time_t time_sec;
- git_time_t timestamp;
- int offset, error_ret=0;
-
- if (!parse_date_basic(date, ×tamp, &offset)) {
- *out = timestamp;
- return 0;
- }
-
- if (time(&time_sec) == -1)
- return -1;
-
- *out = approxidate_str(date, time_sec, &error_ret);
- return error_ret;
-}
-
-int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date)
-{
- int written;
- struct tm gmt;
- time_t t;
-
- assert(out && date);
-
- t = (time_t) (date->time + date->offset * 60);
-
- if (p_gmtime_r (&t, &gmt) == NULL)
- return -1;
-
- written = p_snprintf(out, len, "%.3s, %u %.3s %.4u %02u:%02u:%02u %+03d%02d",
- weekday_names[gmt.tm_wday],
- gmt.tm_mday,
- month_names[gmt.tm_mon],
- gmt.tm_year + 1900,
- gmt.tm_hour, gmt.tm_min, gmt.tm_sec,
- date->offset / 60, date->offset % 60);
-
- if (written < 0 || (written > (int) len - 1))
- return -1;
-
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "delta.h"
-
-/* maximum hash entry list for the same hash bucket */
-#define HASH_LIMIT 64
-
-#define RABIN_SHIFT 23
-#define RABIN_WINDOW 16
-
-static const unsigned int T[256] = {
- 0x00000000, 0xab59b4d1, 0x56b369a2, 0xfdeadd73, 0x063f6795, 0xad66d344,
- 0x508c0e37, 0xfbd5bae6, 0x0c7ecf2a, 0xa7277bfb, 0x5acda688, 0xf1941259,
- 0x0a41a8bf, 0xa1181c6e, 0x5cf2c11d, 0xf7ab75cc, 0x18fd9e54, 0xb3a42a85,
- 0x4e4ef7f6, 0xe5174327, 0x1ec2f9c1, 0xb59b4d10, 0x48719063, 0xe32824b2,
- 0x1483517e, 0xbfdae5af, 0x423038dc, 0xe9698c0d, 0x12bc36eb, 0xb9e5823a,
- 0x440f5f49, 0xef56eb98, 0x31fb3ca8, 0x9aa28879, 0x6748550a, 0xcc11e1db,
- 0x37c45b3d, 0x9c9defec, 0x6177329f, 0xca2e864e, 0x3d85f382, 0x96dc4753,
- 0x6b369a20, 0xc06f2ef1, 0x3bba9417, 0x90e320c6, 0x6d09fdb5, 0xc6504964,
- 0x2906a2fc, 0x825f162d, 0x7fb5cb5e, 0xd4ec7f8f, 0x2f39c569, 0x846071b8,
- 0x798aaccb, 0xd2d3181a, 0x25786dd6, 0x8e21d907, 0x73cb0474, 0xd892b0a5,
- 0x23470a43, 0x881ebe92, 0x75f463e1, 0xdeadd730, 0x63f67950, 0xc8afcd81,
- 0x354510f2, 0x9e1ca423, 0x65c91ec5, 0xce90aa14, 0x337a7767, 0x9823c3b6,
- 0x6f88b67a, 0xc4d102ab, 0x393bdfd8, 0x92626b09, 0x69b7d1ef, 0xc2ee653e,
- 0x3f04b84d, 0x945d0c9c, 0x7b0be704, 0xd05253d5, 0x2db88ea6, 0x86e13a77,
- 0x7d348091, 0xd66d3440, 0x2b87e933, 0x80de5de2, 0x7775282e, 0xdc2c9cff,
- 0x21c6418c, 0x8a9ff55d, 0x714a4fbb, 0xda13fb6a, 0x27f92619, 0x8ca092c8,
- 0x520d45f8, 0xf954f129, 0x04be2c5a, 0xafe7988b, 0x5432226d, 0xff6b96bc,
- 0x02814bcf, 0xa9d8ff1e, 0x5e738ad2, 0xf52a3e03, 0x08c0e370, 0xa39957a1,
- 0x584ced47, 0xf3155996, 0x0eff84e5, 0xa5a63034, 0x4af0dbac, 0xe1a96f7d,
- 0x1c43b20e, 0xb71a06df, 0x4ccfbc39, 0xe79608e8, 0x1a7cd59b, 0xb125614a,
- 0x468e1486, 0xedd7a057, 0x103d7d24, 0xbb64c9f5, 0x40b17313, 0xebe8c7c2,
- 0x16021ab1, 0xbd5bae60, 0x6cb54671, 0xc7ecf2a0, 0x3a062fd3, 0x915f9b02,
- 0x6a8a21e4, 0xc1d39535, 0x3c394846, 0x9760fc97, 0x60cb895b, 0xcb923d8a,
- 0x3678e0f9, 0x9d215428, 0x66f4eece, 0xcdad5a1f, 0x3047876c, 0x9b1e33bd,
- 0x7448d825, 0xdf116cf4, 0x22fbb187, 0x89a20556, 0x7277bfb0, 0xd92e0b61,
- 0x24c4d612, 0x8f9d62c3, 0x7836170f, 0xd36fa3de, 0x2e857ead, 0x85dcca7c,
- 0x7e09709a, 0xd550c44b, 0x28ba1938, 0x83e3ade9, 0x5d4e7ad9, 0xf617ce08,
- 0x0bfd137b, 0xa0a4a7aa, 0x5b711d4c, 0xf028a99d, 0x0dc274ee, 0xa69bc03f,
- 0x5130b5f3, 0xfa690122, 0x0783dc51, 0xacda6880, 0x570fd266, 0xfc5666b7,
- 0x01bcbbc4, 0xaae50f15, 0x45b3e48d, 0xeeea505c, 0x13008d2f, 0xb85939fe,
- 0x438c8318, 0xe8d537c9, 0x153feaba, 0xbe665e6b, 0x49cd2ba7, 0xe2949f76,
- 0x1f7e4205, 0xb427f6d4, 0x4ff24c32, 0xe4abf8e3, 0x19412590, 0xb2189141,
- 0x0f433f21, 0xa41a8bf0, 0x59f05683, 0xf2a9e252, 0x097c58b4, 0xa225ec65,
- 0x5fcf3116, 0xf49685c7, 0x033df00b, 0xa86444da, 0x558e99a9, 0xfed72d78,
- 0x0502979e, 0xae5b234f, 0x53b1fe3c, 0xf8e84aed, 0x17bea175, 0xbce715a4,
- 0x410dc8d7, 0xea547c06, 0x1181c6e0, 0xbad87231, 0x4732af42, 0xec6b1b93,
- 0x1bc06e5f, 0xb099da8e, 0x4d7307fd, 0xe62ab32c, 0x1dff09ca, 0xb6a6bd1b,
- 0x4b4c6068, 0xe015d4b9, 0x3eb80389, 0x95e1b758, 0x680b6a2b, 0xc352defa,
- 0x3887641c, 0x93ded0cd, 0x6e340dbe, 0xc56db96f, 0x32c6cca3, 0x999f7872,
- 0x6475a501, 0xcf2c11d0, 0x34f9ab36, 0x9fa01fe7, 0x624ac294, 0xc9137645,
- 0x26459ddd, 0x8d1c290c, 0x70f6f47f, 0xdbaf40ae, 0x207afa48, 0x8b234e99,
- 0x76c993ea, 0xdd90273b, 0x2a3b52f7, 0x8162e626, 0x7c883b55, 0xd7d18f84,
- 0x2c043562, 0x875d81b3, 0x7ab75cc0, 0xd1eee811
-};
-
-static const unsigned int U[256] = {
- 0x00000000, 0x7eb5200d, 0x5633f4cb, 0x2886d4c6, 0x073e5d47, 0x798b7d4a,
- 0x510da98c, 0x2fb88981, 0x0e7cba8e, 0x70c99a83, 0x584f4e45, 0x26fa6e48,
- 0x0942e7c9, 0x77f7c7c4, 0x5f711302, 0x21c4330f, 0x1cf9751c, 0x624c5511,
- 0x4aca81d7, 0x347fa1da, 0x1bc7285b, 0x65720856, 0x4df4dc90, 0x3341fc9d,
- 0x1285cf92, 0x6c30ef9f, 0x44b63b59, 0x3a031b54, 0x15bb92d5, 0x6b0eb2d8,
- 0x4388661e, 0x3d3d4613, 0x39f2ea38, 0x4747ca35, 0x6fc11ef3, 0x11743efe,
- 0x3eccb77f, 0x40799772, 0x68ff43b4, 0x164a63b9, 0x378e50b6, 0x493b70bb,
- 0x61bda47d, 0x1f088470, 0x30b00df1, 0x4e052dfc, 0x6683f93a, 0x1836d937,
- 0x250b9f24, 0x5bbebf29, 0x73386bef, 0x0d8d4be2, 0x2235c263, 0x5c80e26e,
- 0x740636a8, 0x0ab316a5, 0x2b7725aa, 0x55c205a7, 0x7d44d161, 0x03f1f16c,
- 0x2c4978ed, 0x52fc58e0, 0x7a7a8c26, 0x04cfac2b, 0x73e5d470, 0x0d50f47d,
- 0x25d620bb, 0x5b6300b6, 0x74db8937, 0x0a6ea93a, 0x22e87dfc, 0x5c5d5df1,
- 0x7d996efe, 0x032c4ef3, 0x2baa9a35, 0x551fba38, 0x7aa733b9, 0x041213b4,
- 0x2c94c772, 0x5221e77f, 0x6f1ca16c, 0x11a98161, 0x392f55a7, 0x479a75aa,
- 0x6822fc2b, 0x1697dc26, 0x3e1108e0, 0x40a428ed, 0x61601be2, 0x1fd53bef,
- 0x3753ef29, 0x49e6cf24, 0x665e46a5, 0x18eb66a8, 0x306db26e, 0x4ed89263,
- 0x4a173e48, 0x34a21e45, 0x1c24ca83, 0x6291ea8e, 0x4d29630f, 0x339c4302,
- 0x1b1a97c4, 0x65afb7c9, 0x446b84c6, 0x3adea4cb, 0x1258700d, 0x6ced5000,
- 0x4355d981, 0x3de0f98c, 0x15662d4a, 0x6bd30d47, 0x56ee4b54, 0x285b6b59,
- 0x00ddbf9f, 0x7e689f92, 0x51d01613, 0x2f65361e, 0x07e3e2d8, 0x7956c2d5,
- 0x5892f1da, 0x2627d1d7, 0x0ea10511, 0x7014251c, 0x5facac9d, 0x21198c90,
- 0x099f5856, 0x772a785b, 0x4c921c31, 0x32273c3c, 0x1aa1e8fa, 0x6414c8f7,
- 0x4bac4176, 0x3519617b, 0x1d9fb5bd, 0x632a95b0, 0x42eea6bf, 0x3c5b86b2,
- 0x14dd5274, 0x6a687279, 0x45d0fbf8, 0x3b65dbf5, 0x13e30f33, 0x6d562f3e,
- 0x506b692d, 0x2ede4920, 0x06589de6, 0x78edbdeb, 0x5755346a, 0x29e01467,
- 0x0166c0a1, 0x7fd3e0ac, 0x5e17d3a3, 0x20a2f3ae, 0x08242768, 0x76910765,
- 0x59298ee4, 0x279caee9, 0x0f1a7a2f, 0x71af5a22, 0x7560f609, 0x0bd5d604,
- 0x235302c2, 0x5de622cf, 0x725eab4e, 0x0ceb8b43, 0x246d5f85, 0x5ad87f88,
- 0x7b1c4c87, 0x05a96c8a, 0x2d2fb84c, 0x539a9841, 0x7c2211c0, 0x029731cd,
- 0x2a11e50b, 0x54a4c506, 0x69998315, 0x172ca318, 0x3faa77de, 0x411f57d3,
- 0x6ea7de52, 0x1012fe5f, 0x38942a99, 0x46210a94, 0x67e5399b, 0x19501996,
- 0x31d6cd50, 0x4f63ed5d, 0x60db64dc, 0x1e6e44d1, 0x36e89017, 0x485db01a,
- 0x3f77c841, 0x41c2e84c, 0x69443c8a, 0x17f11c87, 0x38499506, 0x46fcb50b,
- 0x6e7a61cd, 0x10cf41c0, 0x310b72cf, 0x4fbe52c2, 0x67388604, 0x198da609,
- 0x36352f88, 0x48800f85, 0x6006db43, 0x1eb3fb4e, 0x238ebd5d, 0x5d3b9d50,
- 0x75bd4996, 0x0b08699b, 0x24b0e01a, 0x5a05c017, 0x728314d1, 0x0c3634dc,
- 0x2df207d3, 0x534727de, 0x7bc1f318, 0x0574d315, 0x2acc5a94, 0x54797a99,
- 0x7cffae5f, 0x024a8e52, 0x06852279, 0x78300274, 0x50b6d6b2, 0x2e03f6bf,
- 0x01bb7f3e, 0x7f0e5f33, 0x57888bf5, 0x293dabf8, 0x08f998f7, 0x764cb8fa,
- 0x5eca6c3c, 0x207f4c31, 0x0fc7c5b0, 0x7172e5bd, 0x59f4317b, 0x27411176,
- 0x1a7c5765, 0x64c97768, 0x4c4fa3ae, 0x32fa83a3, 0x1d420a22, 0x63f72a2f,
- 0x4b71fee9, 0x35c4dee4, 0x1400edeb, 0x6ab5cde6, 0x42331920, 0x3c86392d,
- 0x133eb0ac, 0x6d8b90a1, 0x450d4467, 0x3bb8646a
-};
-
-struct index_entry {
- const unsigned char *ptr;
- unsigned int val;
- struct index_entry *next;
-};
-
-struct git_delta_index {
- unsigned long memsize;
- const void *src_buf;
- size_t src_size;
- unsigned int hash_mask;
- struct index_entry *hash[GIT_FLEX_ARRAY];
-};
-
-static int lookup_index_alloc(
- void **out, unsigned long *out_len, size_t entries, size_t hash_count)
-{
- size_t entries_len, hash_len, index_len;
-
- GITERR_CHECK_ALLOC_MULTIPLY(&entries_len, entries, sizeof(struct index_entry));
- GITERR_CHECK_ALLOC_MULTIPLY(&hash_len, hash_count, sizeof(struct index_entry *));
-
- GITERR_CHECK_ALLOC_ADD(&index_len, sizeof(struct git_delta_index), entries_len);
- GITERR_CHECK_ALLOC_ADD(&index_len, index_len, hash_len);
-
- if (!git__is_ulong(index_len)) {
- giterr_set(GITERR_NOMEMORY, "Overly large delta");
- return -1;
- }
-
- *out = git__malloc(index_len);
- GITERR_CHECK_ALLOC(*out);
-
- *out_len = index_len;
- return 0;
-}
-
-int git_delta_index_init(
- git_delta_index **out, const void *buf, size_t bufsize)
-{
- unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
- const unsigned char *data, *buffer = buf;
- struct git_delta_index *index;
- struct index_entry *entry, **hash;
- void *mem;
- unsigned long memsize;
-
- *out = NULL;
-
- if (!buf || !bufsize)
- return 0;
-
- /* Determine index hash size. Note that indexing skips the
- first byte to allow for optimizing the rabin polynomial
- initialization in create_delta(). */
- entries = (unsigned int)(bufsize - 1) / RABIN_WINDOW;
- if (bufsize >= 0xffffffffUL) {
- /*
- * Current delta format can't encode offsets into
- * reference buffer with more than 32 bits.
- */
- entries = 0xfffffffeU / RABIN_WINDOW;
- }
- hsize = entries / 4;
- for (i = 4; i < 31 && (1u << i) < hsize; i++);
- hsize = 1 << i;
- hmask = hsize - 1;
-
- if (lookup_index_alloc(&mem, &memsize, entries, hsize) < 0)
- return -1;
-
- index = mem;
- mem = index->hash;
- hash = mem;
- mem = hash + hsize;
- entry = mem;
-
- index->memsize = memsize;
- index->src_buf = buf;
- index->src_size = bufsize;
- index->hash_mask = hmask;
- memset(hash, 0, hsize * sizeof(*hash));
-
- /* allocate an array to count hash entries */
- hash_count = git__calloc(hsize, sizeof(*hash_count));
- if (!hash_count) {
- git__free(index);
- return -1;
- }
-
- /* then populate the index */
- prev_val = ~0;
- for (data = buffer + entries * RABIN_WINDOW - RABIN_WINDOW;
- data >= buffer;
- data -= RABIN_WINDOW) {
- unsigned int val = 0;
- for (i = 1; i <= RABIN_WINDOW; i++)
- val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT];
- if (val == prev_val) {
- /* keep the lowest of consecutive identical blocks */
- entry[-1].ptr = data + RABIN_WINDOW;
- } else {
- prev_val = val;
- i = val & hmask;
- entry->ptr = data + RABIN_WINDOW;
- entry->val = val;
- entry->next = hash[i];
- hash[i] = entry++;
- hash_count[i]++;
- }
- }
-
- /*
- * Determine a limit on the number of entries in the same hash
- * bucket. This guard us against patological data sets causing
- * really bad hash distribution with most entries in the same hash
- * bucket that would bring us to O(m*n) computing costs (m and n
- * corresponding to reference and target buffer sizes).
- *
- * Make sure none of the hash buckets has more entries than
- * we're willing to test. Otherwise we cull the entry list
- * uniformly to still preserve a good repartition across
- * the reference buffer.
- */
- for (i = 0; i < hsize; i++) {
- if (hash_count[i] < HASH_LIMIT)
- continue;
-
- entry = hash[i];
- do {
- struct index_entry *keep = entry;
- int skip = hash_count[i] / HASH_LIMIT / 2;
- do {
- entry = entry->next;
- } while(--skip && entry);
- keep->next = entry;
- } while (entry);
- }
- git__free(hash_count);
-
- *out = index;
- return 0;
-}
-
-void git_delta_index_free(git_delta_index *index)
-{
- git__free(index);
-}
-
-size_t git_delta_index_size(git_delta_index *index)
-{
- assert(index);
-
- return index->memsize;
-}
-
-/*
- * The maximum size for any opcode sequence, including the initial header
- * plus rabin window plus biggest copy.
- */
-#define MAX_OP_SIZE (5 + 5 + 1 + RABIN_WINDOW + 7)
-
-int git_delta_create_from_index(
- void **out,
- size_t *out_len,
- const struct git_delta_index *index,
- const void *trg_buf,
- size_t trg_size,
- size_t max_size)
-{
- unsigned int i, bufpos, bufsize, moff, msize, val;
- int inscnt;
- const unsigned char *ref_data, *ref_top, *data, *top;
- unsigned char *buf;
-
- *out = NULL;
- *out_len = 0;
-
- if (!trg_buf || !trg_size)
- return 0;
-
- bufpos = 0;
- bufsize = 8192;
- if (max_size && bufsize >= max_size)
- bufsize = (unsigned int)(max_size + MAX_OP_SIZE + 1);
- buf = git__malloc(bufsize);
- GITERR_CHECK_ALLOC(buf);
-
- /* store reference buffer size */
- i = index->src_size;
- while (i >= 0x80) {
- buf[bufpos++] = i | 0x80;
- i >>= 7;
- }
- buf[bufpos++] = i;
-
- /* store target buffer size */
- i = trg_size;
- while (i >= 0x80) {
- buf[bufpos++] = i | 0x80;
- i >>= 7;
- }
- buf[bufpos++] = i;
-
- ref_data = index->src_buf;
- ref_top = ref_data + index->src_size;
- data = trg_buf;
- top = (const unsigned char *) trg_buf + trg_size;
-
- bufpos++;
- val = 0;
- for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) {
- buf[bufpos++] = *data;
- val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
- }
- inscnt = i;
-
- moff = 0;
- msize = 0;
- while (data < top) {
- if (msize < 4096) {
- struct index_entry *entry;
- val ^= U[data[-RABIN_WINDOW]];
- val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
- i = val & index->hash_mask;
- for (entry = index->hash[i]; entry; entry = entry->next) {
- const unsigned char *ref = entry->ptr;
- const unsigned char *src = data;
- unsigned int ref_size = (unsigned int)(ref_top - ref);
- if (entry->val != val)
- continue;
- if (ref_size > (unsigned int)(top - src))
- ref_size = (unsigned int)(top - src);
- if (ref_size <= msize)
- break;
- while (ref_size-- && *src++ == *ref)
- ref++;
- if (msize < (unsigned int)(ref - entry->ptr)) {
- /* this is our best match so far */
- msize = (unsigned int)(ref - entry->ptr);
- moff = (unsigned int)(entry->ptr - ref_data);
- if (msize >= 4096) /* good enough */
- break;
- }
- }
- }
-
- if (msize < 4) {
- if (!inscnt)
- bufpos++;
- buf[bufpos++] = *data++;
- inscnt++;
- if (inscnt == 0x7f) {
- buf[bufpos - inscnt - 1] = inscnt;
- inscnt = 0;
- }
- msize = 0;
- } else {
- unsigned int left;
- unsigned char *op;
-
- if (inscnt) {
- while (moff && ref_data[moff-1] == data[-1]) {
- /* we can match one byte back */
- msize++;
- moff--;
- data--;
- bufpos--;
- if (--inscnt)
- continue;
- bufpos--; /* remove count slot */
- inscnt--; /* make it -1 */
- break;
- }
- buf[bufpos - inscnt - 1] = inscnt;
- inscnt = 0;
- }
-
- /* A copy op is currently limited to 64KB (pack v2) */
- left = (msize < 0x10000) ? 0 : (msize - 0x10000);
- msize -= left;
-
- op = buf + bufpos++;
- i = 0x80;
-
- if (moff & 0x000000ff)
- buf[bufpos++] = moff >> 0, i |= 0x01;
- if (moff & 0x0000ff00)
- buf[bufpos++] = moff >> 8, i |= 0x02;
- if (moff & 0x00ff0000)
- buf[bufpos++] = moff >> 16, i |= 0x04;
- if (moff & 0xff000000)
- buf[bufpos++] = moff >> 24, i |= 0x08;
-
- if (msize & 0x00ff)
- buf[bufpos++] = msize >> 0, i |= 0x10;
- if (msize & 0xff00)
- buf[bufpos++] = msize >> 8, i |= 0x20;
-
- *op = i;
-
- data += msize;
- moff += msize;
- msize = left;
-
- if (msize < 4096) {
- int j;
- val = 0;
- for (j = -RABIN_WINDOW; j < 0; j++)
- val = ((val << 8) | data[j])
- ^ T[val >> RABIN_SHIFT];
- }
- }
-
- if (bufpos >= bufsize - MAX_OP_SIZE) {
- void *tmp = buf;
- bufsize = bufsize * 3 / 2;
- if (max_size && bufsize >= max_size)
- bufsize = max_size + MAX_OP_SIZE + 1;
- if (max_size && bufpos > max_size)
- break;
- buf = git__realloc(buf, bufsize);
- if (!buf) {
- git__free(tmp);
- return -1;
- }
- }
- }
-
- if (inscnt)
- buf[bufpos - inscnt - 1] = inscnt;
-
- if (max_size && bufpos > max_size) {
- giterr_set(GITERR_NOMEMORY, "delta would be larger than maximum size");
- git__free(buf);
- return GIT_EBUFS;
- }
-
- *out_len = bufpos;
- *out = buf;
- return 0;
-}
-
-/*
-* Delta application was heavily cribbed from BinaryDelta.java in JGit, which
-* itself was heavily cribbed from <code>patch-delta.c</code> in the
-* GIT project. The original delta patching code was written by
-* Nicolas Pitre <nico@cam.org>.
-*/
-
-static int hdr_sz(
- size_t *size,
- const unsigned char **delta,
- const unsigned char *end)
-{
- const unsigned char *d = *delta;
- size_t r = 0;
- unsigned int c, shift = 0;
-
- do {
- if (d == end) {
- giterr_set(GITERR_INVALID, "truncated delta");
- return -1;
- }
-
- c = *d++;
- r |= (c & 0x7f) << shift;
- shift += 7;
- } while (c & 0x80);
- *delta = d;
- *size = r;
- return 0;
-}
-
-int git_delta_read_header(
- size_t *base_out,
- size_t *result_out,
- const unsigned char *delta,
- size_t delta_len)
-{
- const unsigned char *delta_end = delta + delta_len;
- if ((hdr_sz(base_out, &delta, delta_end) < 0) ||
- (hdr_sz(result_out, &delta, delta_end) < 0))
- return -1;
- return 0;
-}
-
-#define DELTA_HEADER_BUFFER_LEN 16
-int git_delta_read_header_fromstream(
- size_t *base_sz, size_t *res_sz, git_packfile_stream *stream)
-{
- static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN;
- unsigned char buffer[DELTA_HEADER_BUFFER_LEN];
- const unsigned char *delta, *delta_end;
- size_t len;
- ssize_t read;
-
- len = read = 0;
- while (len < buffer_len) {
- read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len);
-
- if (read == 0)
- break;
-
- if (read == GIT_EBUFS)
- continue;
-
- len += read;
- }
-
- delta = buffer;
- delta_end = delta + len;
- if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
- (hdr_sz(res_sz, &delta, delta_end) < 0))
- return -1;
-
- return 0;
-}
-
-int git_delta_apply(
- void **out,
- size_t *out_len,
- const unsigned char *base,
- size_t base_len,
- const unsigned char *delta,
- size_t delta_len)
-{
- const unsigned char *delta_end = delta + delta_len;
- size_t base_sz, res_sz, alloc_sz;
- unsigned char *res_dp;
-
- *out = NULL;
- *out_len = 0;
-
- /* Check that the base size matches the data we were given;
- * if not we would underflow while accessing data from the
- * base object, resulting in data corruption or segfault.
- */
- if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
- giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
- return -1;
- }
-
- if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
- giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
- return -1;
- }
-
- GITERR_CHECK_ALLOC_ADD(&alloc_sz, res_sz, 1);
- res_dp = git__malloc(alloc_sz);
- GITERR_CHECK_ALLOC(res_dp);
-
- res_dp[res_sz] = '\0';
- *out = res_dp;
- *out_len = res_sz;
-
- while (delta < delta_end) {
- unsigned char cmd = *delta++;
- if (cmd & 0x80) {
- /* cmd is a copy instruction; copy from the base.
- */
- size_t off = 0, len = 0;
-
- if (cmd & 0x01) off = *delta++;
- if (cmd & 0x02) off |= *delta++ << 8UL;
- if (cmd & 0x04) off |= *delta++ << 16UL;
- if (cmd & 0x08) off |= *delta++ << 24UL;
-
- if (cmd & 0x10) len = *delta++;
- if (cmd & 0x20) len |= *delta++ << 8UL;
- if (cmd & 0x40) len |= *delta++ << 16UL;
- if (!len) len = 0x10000;
-
- if (base_len < off + len || res_sz < len)
- goto fail;
- memcpy(res_dp, base + off, len);
- res_dp += len;
- res_sz -= len;
-
- }
- else if (cmd) {
- /* cmd is a literal insert instruction; copy from
- * the delta stream itself.
- */
- if (delta_end - delta < cmd || res_sz < cmd)
- goto fail;
- memcpy(res_dp, delta, cmd);
- delta += cmd;
- res_dp += cmd;
- res_sz -= cmd;
-
- }
- else {
- /* cmd == 0 is reserved for future encodings.
- */
- goto fail;
- }
- }
-
- if (delta != delta_end || res_sz)
- goto fail;
- return 0;
-
-fail:
- git__free(*out);
-
- *out = NULL;
- *out_len = 0;
-
- giterr_set(GITERR_INVALID, "Failed to apply delta");
- return -1;
-}
+++ /dev/null
-/*
- * diff-delta code taken from git.git. See diff-delta.c for details.
- *
- */
-#ifndef INCLUDE_git_delta_h__
-#define INCLUDE_git_delta_h__
-
-#include "common.h"
-#include "pack.h"
-
-typedef struct git_delta_index git_delta_index;
-
-/*
- * git_delta_index_init: compute index data from given buffer
- *
- * This returns a pointer to a struct delta_index that should be passed to
- * subsequent create_delta() calls, or to free_delta_index(). A NULL pointer
- * is returned on failure. The given buffer must not be freed nor altered
- * before free_delta_index() is called. The returned pointer must be freed
- * using free_delta_index().
- */
-extern int git_delta_index_init(
- git_delta_index **out, const void *buf, size_t bufsize);
-
-/*
- * Free the index created by git_delta_index_init()
- */
-extern void git_delta_index_free(git_delta_index *index);
-
-/*
- * Returns memory usage of delta index.
- */
-extern size_t git_delta_index_size(git_delta_index *index);
-
-/*
- * create_delta: create a delta from given index for the given buffer
- *
- * This function may be called multiple times with different buffers using
- * the same delta_index pointer. If max_delta_size is non-zero and the
- * resulting delta is to be larger than max_delta_size then NULL is returned.
- * On success, a non-NULL pointer to the buffer with the delta data is
- * returned and *delta_size is updated with its size. The returned buffer
- * must be freed by the caller.
- */
-extern int git_delta_create_from_index(
- void **out,
- size_t *out_size,
- const struct git_delta_index *index,
- const void *buf,
- size_t bufsize,
- size_t max_delta_size);
-
-/*
- * diff_delta: create a delta from source buffer to target buffer
- *
- * If max_delta_size is non-zero and the resulting delta is to be larger
- * than max_delta_size then GIT_EBUFS is returned. On success, a non-NULL
- * pointer to the buffer with the delta data is returned and *delta_size is
- * updated with its size. The returned buffer must be freed by the caller.
- */
-GIT_INLINE(int) git_delta(
- void **out, size_t *out_len,
- const void *src_buf, size_t src_bufsize,
- const void *trg_buf, size_t trg_bufsize,
- size_t max_delta_size)
-{
- git_delta_index *index;
- int error = 0;
-
- *out = NULL;
- *out_len = 0;
-
- if ((error = git_delta_index_init(&index, src_buf, src_bufsize)) < 0)
- return error;
-
- if (index) {
- error = git_delta_create_from_index(out, out_len,
- index, trg_buf, trg_bufsize, max_delta_size);
-
- git_delta_index_free(index);
- }
-
- return error;
-}
-
-/* the smallest possible delta size is 4 bytes */
-#define GIT_DELTA_SIZE_MIN 4
-
-/**
-* Apply a git binary delta to recover the original content.
-* The caller is responsible for freeing the returned buffer.
-*
-* @param out the output buffer
-* @param out_len the length of the output buffer
-* @param base the base to copy from during copy instructions.
-* @param base_len number of bytes available at base.
-* @param delta the delta to execute copy/insert instructions from.
-* @param delta_len total number of bytes in the delta.
-* @return 0 on success or an error code
-*/
-extern int git_delta_apply(
- void **out,
- size_t *out_len,
- const unsigned char *base,
- size_t base_len,
- const unsigned char *delta,
- size_t delta_len);
-
-/**
-* Read the header of a git binary delta.
-*
-* @param base_out pointer to store the base size field.
-* @param result_out pointer to store the result size field.
-* @param delta the delta to execute copy/insert instructions from.
-* @param delta_len total number of bytes in the delta.
-* @return 0 on success or an error code
-*/
-extern int git_delta_read_header(
- size_t *base_out,
- size_t *result_out,
- const unsigned char *delta,
- size_t delta_len);
-
-/**
- * Read the header of a git binary delta
- *
- * This variant reads just enough from the packfile stream to read the
- * delta header.
- */
-extern int git_delta_read_header_fromstream(
- size_t *base_out,
- size_t *result_out,
- git_packfile_stream *stream);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "git2/describe.h"
-#include "git2/strarray.h"
-#include "git2/diff.h"
-#include "git2/status.h"
-
-#include "common.h"
-#include "commit.h"
-#include "commit_list.h"
-#include "oidmap.h"
-#include "refs.h"
-#include "revwalk.h"
-#include "tag.h"
-#include "vector.h"
-#include "repository.h"
-
-GIT__USE_OIDMAP
-
-/* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */
-
-struct commit_name {
- git_tag *tag;
- unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
- unsigned name_checked:1;
- git_oid sha1;
- char *path;
-
- /* Khash workaround. They original key has to still be reachable */
- git_oid peeled;
-};
-
-static void *oidmap_value_bykey(git_oidmap *map, const git_oid *key)
-{
- khint_t pos = git_oidmap_lookup_index(map, key);
-
- if (!git_oidmap_valid_index(map, pos))
- return NULL;
-
- return git_oidmap_value_at(map, pos);
-}
-
-static struct commit_name *find_commit_name(
- git_oidmap *names,
- const git_oid *peeled)
-{
- return (struct commit_name *)(oidmap_value_bykey(names, peeled));
-}
-
-static int replace_name(
- git_tag **tag,
- git_repository *repo,
- struct commit_name *e,
- unsigned int prio,
- const git_oid *sha1)
-{
- git_time_t e_time = 0, t_time = 0;
-
- if (!e || e->prio < prio)
- return 1;
-
- if (e->prio == 2 && prio == 2) {
- /* Multiple annotated tags point to the same commit.
- * Select one to keep based upon their tagger date.
- */
- git_tag *t = NULL;
-
- if (!e->tag) {
- if (git_tag_lookup(&t, repo, &e->sha1) < 0)
- return 1;
- e->tag = t;
- }
-
- if (git_tag_lookup(&t, repo, sha1) < 0)
- return 0;
-
- *tag = t;
-
- if (e->tag->tagger)
- e_time = e->tag->tagger->when.time;
-
- if (t->tagger)
- t_time = t->tagger->when.time;
-
- if (e_time < t_time)
- return 1;
- }
-
- return 0;
-}
-
-static int add_to_known_names(
- git_repository *repo,
- git_oidmap *names,
- const char *path,
- const git_oid *peeled,
- unsigned int prio,
- const git_oid *sha1)
-{
- struct commit_name *e = find_commit_name(names, peeled);
- bool found = (e != NULL);
-
- git_tag *tag = NULL;
- if (replace_name(&tag, repo, e, prio, sha1)) {
- if (!found) {
- e = git__malloc(sizeof(struct commit_name));
- GITERR_CHECK_ALLOC(e);
-
- e->path = NULL;
- e->tag = NULL;
- }
-
- if (e->tag)
- git_tag_free(e->tag);
- e->tag = tag;
- e->prio = prio;
- e->name_checked = 0;
- git_oid_cpy(&e->sha1, sha1);
- git__free(e->path);
- e->path = git__strdup(path);
- git_oid_cpy(&e->peeled, peeled);
-
- if (!found) {
- int ret;
-
- git_oidmap_insert(names, &e->peeled, e, ret);
- if (ret < 0)
- return -1;
- }
- }
- else
- git_tag_free(tag);
-
- return 0;
-}
-
-static int retrieve_peeled_tag_or_object_oid(
- git_oid *peeled_out,
- git_oid *ref_target_out,
- git_repository *repo,
- const char *refname)
-{
- git_reference *ref;
- git_object *peeled = NULL;
- int error;
-
- if ((error = git_reference_lookup_resolved(&ref, repo, refname, -1)) < 0)
- return error;
-
- if ((error = git_reference_peel(&peeled, ref, GIT_OBJ_ANY)) < 0)
- goto cleanup;
-
- git_oid_cpy(ref_target_out, git_reference_target(ref));
- git_oid_cpy(peeled_out, git_object_id(peeled));
-
- if (git_oid_cmp(ref_target_out, peeled_out) != 0)
- error = 1; /* The reference was pointing to a annotated tag */
- else
- error = 0; /* Any other object */
-
-cleanup:
- git_reference_free(ref);
- git_object_free(peeled);
- return error;
-}
-
-struct git_describe_result {
- int dirty;
- int exact_match;
- int fallback_to_id;
- git_oid commit_id;
- git_repository *repo;
- struct commit_name *name;
- struct possible_tag *tag;
-};
-
-struct get_name_data
-{
- git_describe_options *opts;
- git_repository *repo;
- git_oidmap *names;
- git_describe_result *result;
-};
-
-static int commit_name_dup(struct commit_name **out, struct commit_name *in)
-{
- struct commit_name *name;
-
- name = git__malloc(sizeof(struct commit_name));
- GITERR_CHECK_ALLOC(name);
-
- memcpy(name, in, sizeof(struct commit_name));
- name->tag = NULL;
- name->path = NULL;
-
- if (in->tag && git_tag_dup(&name->tag, in->tag) < 0)
- return -1;
-
- name->path = git__strdup(in->path);
- GITERR_CHECK_ALLOC(name->path);
-
- *out = name;
- return 0;
-}
-
-static int get_name(const char *refname, void *payload)
-{
- struct get_name_data *data;
- bool is_tag, is_annotated, all;
- git_oid peeled, sha1;
- unsigned int prio;
- int error = 0;
-
- data = (struct get_name_data *)payload;
- is_tag = !git__prefixcmp(refname, GIT_REFS_TAGS_DIR);
- all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
-
- /* Reject anything outside refs/tags/ unless --all */
- if (!all && !is_tag)
- return 0;
-
- /* Accept only tags that match the pattern, if given */
- if (data->opts->pattern && (!is_tag || p_fnmatch(data->opts->pattern,
- refname + strlen(GIT_REFS_TAGS_DIR), 0)))
- return 0;
-
- /* Is it annotated? */
- if ((error = retrieve_peeled_tag_or_object_oid(
- &peeled, &sha1, data->repo, refname)) < 0)
- return error;
-
- is_annotated = error;
-
- /*
- * By default, we only use annotated tags, but with --tags
- * we fall back to lightweight ones (even without --tags,
- * we still remember lightweight ones, only to give hints
- * in an error message). --all allows any refs to be used.
- */
- if (is_annotated)
- prio = 2;
- else if (is_tag)
- prio = 1;
- else
- prio = 0;
-
- add_to_known_names(data->repo, data->names,
- all ? refname + strlen(GIT_REFS_DIR) : refname + strlen(GIT_REFS_TAGS_DIR),
- &peeled, prio, &sha1);
- return 0;
-}
-
-struct possible_tag {
- struct commit_name *name;
- int depth;
- int found_order;
- unsigned flag_within;
-};
-
-static int possible_tag_dup(struct possible_tag **out, struct possible_tag *in)
-{
- struct possible_tag *tag;
- int error;
-
- tag = git__malloc(sizeof(struct possible_tag));
- GITERR_CHECK_ALLOC(tag);
-
- memcpy(tag, in, sizeof(struct possible_tag));
- tag->name = NULL;
-
- if ((error = commit_name_dup(&tag->name, in->name)) < 0) {
- git__free(tag);
- *out = NULL;
- return error;
- }
-
- *out = tag;
- return 0;
-}
-
-static int compare_pt(const void *a_, const void *b_)
-{
- struct possible_tag *a = (struct possible_tag *)a_;
- struct possible_tag *b = (struct possible_tag *)b_;
- if (a->depth != b->depth)
- return a->depth - b->depth;
- if (a->found_order != b->found_order)
- return a->found_order - b->found_order;
- return 0;
-}
-
-#define SEEN (1u << 0)
-
-static unsigned long finish_depth_computation(
- git_pqueue *list,
- git_revwalk *walk,
- struct possible_tag *best)
-{
- unsigned long seen_commits = 0;
- int error, i;
-
- while (git_pqueue_size(list) > 0) {
- git_commit_list_node *c = git_pqueue_pop(list);
- seen_commits++;
- if (c->flags & best->flag_within) {
- size_t index = 0;
- while (git_pqueue_size(list) > index) {
- git_commit_list_node *i = git_pqueue_get(list, index);
- if (!(i->flags & best->flag_within))
- break;
- index++;
- }
- if (index > git_pqueue_size(list))
- break;
- } else
- best->depth++;
- for (i = 0; i < c->out_degree; i++) {
- git_commit_list_node *p = c->parents[i];
- if ((error = git_commit_list_parse(walk, p)) < 0)
- return error;
- if (!(p->flags & SEEN))
- if ((error = git_pqueue_insert(list, p)) < 0)
- return error;
- p->flags |= c->flags;
- }
- }
- return seen_commits;
-}
-
-static int display_name(git_buf *buf, git_repository *repo, struct commit_name *n)
-{
- if (n->prio == 2 && !n->tag) {
- if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) {
- giterr_set(GITERR_TAG, "Annotated tag '%s' not available", n->path);
- return -1;
- }
- }
-
- if (n->tag && !n->name_checked) {
- if (!git_tag_name(n->tag)) {
- giterr_set(GITERR_TAG, "Annotated tag '%s' has no embedded name", n->path);
- return -1;
- }
-
- /* TODO: Cope with warnings
- if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
- warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path);
- */
-
- n->name_checked = 1;
- }
-
- if (n->tag)
- git_buf_printf(buf, "%s", git_tag_name(n->tag));
- else
- git_buf_printf(buf, "%s", n->path);
-
- return 0;
-}
-
-static int find_unique_abbrev_size(
- int *out,
- git_repository *repo,
- const git_oid *oid_in,
- int abbreviated_size)
-{
- size_t size = abbreviated_size;
- git_odb *odb;
- git_oid dummy;
- int error;
-
- if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
- return error;
-
- while (size < GIT_OID_HEXSZ) {
- if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) {
- *out = (int) size;
- return 0;
- }
-
- /* If the error wasn't that it's not unique, then it's a proper error */
- if (error != GIT_EAMBIGUOUS)
- return error;
-
- /* Try again with a larger size */
- size++;
- }
-
- /* If we didn't find any shorter prefix, we have to do the whole thing */
- *out = GIT_OID_HEXSZ;
-
- return 0;
-}
-
-static int show_suffix(
- git_buf *buf,
- int depth,
- git_repository *repo,
- const git_oid* id,
- size_t abbrev_size)
-{
- int error, size = 0;
-
- char hex_oid[GIT_OID_HEXSZ];
-
- if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0)
- return error;
-
- git_oid_fmt(hex_oid, id);
-
- git_buf_printf(buf, "-%d-g", depth);
-
- git_buf_put(buf, hex_oid, size);
-
- return git_buf_oom(buf) ? -1 : 0;
-}
-
-#define MAX_CANDIDATES_TAGS FLAG_BITS - 1
-
-static int describe_not_found(const git_oid *oid, const char *message_format) {
- char oid_str[GIT_OID_HEXSZ + 1];
- git_oid_tostr(oid_str, sizeof(oid_str), oid);
-
- giterr_set(GITERR_DESCRIBE, message_format, oid_str);
- return GIT_ENOTFOUND;
-}
-
-static int describe(
- struct get_name_data *data,
- git_commit *commit)
-{
- struct commit_name *n;
- struct possible_tag *best;
- bool all, tags;
- git_revwalk *walk = NULL;
- git_pqueue list;
- git_commit_list_node *cmit, *gave_up_on = NULL;
- git_vector all_matches = GIT_VECTOR_INIT;
- unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
- unsigned long seen_commits = 0; /* TODO: Check long */
- unsigned int unannotated_cnt = 0;
- int error;
-
- if (git_vector_init(&all_matches, MAX_CANDIDATES_TAGS, compare_pt) < 0)
- return -1;
-
- if ((error = git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp)) < 0)
- goto cleanup;
-
- all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
- tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS;
-
- git_oid_cpy(&data->result->commit_id, git_commit_id(commit));
-
- n = find_commit_name(data->names, git_commit_id(commit));
- if (n && (tags || all || n->prio == 2)) {
- /*
- * Exact match to an existing ref.
- */
- data->result->exact_match = 1;
- if ((error = commit_name_dup(&data->result->name, n)) < 0)
- goto cleanup;
-
- goto cleanup;
- }
-
- if (!data->opts->max_candidates_tags) {
- error = describe_not_found(
- git_commit_id(commit),
- "Cannot describe - no tag exactly matches '%s'");
-
- goto cleanup;
- }
-
- if ((error = git_revwalk_new(&walk, git_commit_owner(commit))) < 0)
- goto cleanup;
-
- if ((cmit = git_revwalk__commit_lookup(walk, git_commit_id(commit))) == NULL)
- goto cleanup;
-
- if ((error = git_commit_list_parse(walk, cmit)) < 0)
- goto cleanup;
-
- cmit->flags = SEEN;
-
- if ((error = git_pqueue_insert(&list, cmit)) < 0)
- goto cleanup;
-
- while (git_pqueue_size(&list) > 0)
- {
- int i;
-
- git_commit_list_node *c = (git_commit_list_node *)git_pqueue_pop(&list);
- seen_commits++;
-
- n = find_commit_name(data->names, &c->oid);
-
- if (n) {
- if (!tags && !all && n->prio < 2) {
- unannotated_cnt++;
- } else if (match_cnt < data->opts->max_candidates_tags) {
- struct possible_tag *t = git__malloc(sizeof(struct commit_name));
- GITERR_CHECK_ALLOC(t);
- if ((error = git_vector_insert(&all_matches, t)) < 0)
- goto cleanup;
-
- match_cnt++;
-
- t->name = n;
- t->depth = seen_commits - 1;
- t->flag_within = 1u << match_cnt;
- t->found_order = match_cnt;
- c->flags |= t->flag_within;
- if (n->prio == 2)
- annotated_cnt++;
- }
- else {
- gave_up_on = c;
- break;
- }
- }
-
- for (cur_match = 0; cur_match < match_cnt; cur_match++) {
- struct possible_tag *t = git_vector_get(&all_matches, cur_match);
- if (!(c->flags & t->flag_within))
- t->depth++;
- }
-
- if (annotated_cnt && (git_pqueue_size(&list) == 0)) {
- /*
- if (debug) {
- char oid_str[GIT_OID_HEXSZ + 1];
- git_oid_tostr(oid_str, sizeof(oid_str), &c->oid);
-
- fprintf(stderr, "finished search at %s\n", oid_str);
- }
- */
- break;
- }
- for (i = 0; i < c->out_degree; i++) {
- git_commit_list_node *p = c->parents[i];
- if ((error = git_commit_list_parse(walk, p)) < 0)
- goto cleanup;
- if (!(p->flags & SEEN))
- if ((error = git_pqueue_insert(&list, p)) < 0)
- goto cleanup;
- p->flags |= c->flags;
-
- if (data->opts->only_follow_first_parent)
- break;
- }
- }
-
- if (!match_cnt) {
- if (data->opts->show_commit_oid_as_fallback) {
- data->result->fallback_to_id = 1;
- git_oid_cpy(&data->result->commit_id, &cmit->oid);
-
- goto cleanup;
- }
- if (unannotated_cnt) {
- error = describe_not_found(git_commit_id(commit),
- "Cannot describe - "
- "No annotated tags can describe '%s'."
- "However, there were unannotated tags.");
- goto cleanup;
- }
- else {
- error = describe_not_found(git_commit_id(commit),
- "Cannot describe - "
- "No tags can describe '%s'.");
- goto cleanup;
- }
- }
-
- git_vector_sort(&all_matches);
-
- best = (struct possible_tag *)git_vector_get(&all_matches, 0);
-
- if (gave_up_on) {
- if ((error = git_pqueue_insert(&list, gave_up_on)) < 0)
- goto cleanup;
- seen_commits--;
- }
- if ((error = finish_depth_computation(
- &list, walk, best)) < 0)
- goto cleanup;
-
- seen_commits += error;
- if ((error = possible_tag_dup(&data->result->tag, best)) < 0)
- goto cleanup;
-
- /*
- {
- static const char *prio_names[] = {
- "head", "lightweight", "annotated",
- };
-
- char oid_str[GIT_OID_HEXSZ + 1];
-
- if (debug) {
- for (cur_match = 0; cur_match < match_cnt; cur_match++) {
- struct possible_tag *t = (struct possible_tag *)git_vector_get(&all_matches, cur_match);
- fprintf(stderr, " %-11s %8d %s\n",
- prio_names[t->name->prio],
- t->depth, t->name->path);
- }
- fprintf(stderr, "traversed %lu commits\n", seen_commits);
- if (gave_up_on) {
- git_oid_tostr(oid_str, sizeof(oid_str), &gave_up_on->oid);
- fprintf(stderr,
- "more than %i tags found; listed %i most recent\n"
- "gave up search at %s\n",
- data->opts->max_candidates_tags, data->opts->max_candidates_tags,
- oid_str);
- }
- }
- }
- */
-
- git_oid_cpy(&data->result->commit_id, &cmit->oid);
-
-cleanup:
- {
- size_t i;
- struct possible_tag *match;
- git_vector_foreach(&all_matches, i, match) {
- git__free(match);
- }
- }
- git_vector_free(&all_matches);
- git_pqueue_free(&list);
- git_revwalk_free(walk);
- return error;
-}
-
-static int normalize_options(
- git_describe_options *dst,
- const git_describe_options *src)
-{
- git_describe_options default_options = GIT_DESCRIBE_OPTIONS_INIT;
- if (!src) src = &default_options;
-
- *dst = *src;
-
- if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS)
- dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS;
-
- return 0;
-}
-
-int git_describe_commit(
- git_describe_result **result,
- git_object *committish,
- git_describe_options *opts)
-{
- struct get_name_data data;
- struct commit_name *name;
- git_commit *commit;
- int error = -1;
- git_describe_options normalized;
-
- assert(committish);
-
- data.result = git__calloc(1, sizeof(git_describe_result));
- GITERR_CHECK_ALLOC(data.result);
- data.result->repo = git_object_owner(committish);
-
- data.repo = git_object_owner(committish);
-
- if ((error = normalize_options(&normalized, opts)) < 0)
- return error;
-
- GITERR_CHECK_VERSION(
- &normalized,
- GIT_DESCRIBE_OPTIONS_VERSION,
- "git_describe_options");
- data.opts = &normalized;
-
- data.names = git_oidmap_alloc();
- GITERR_CHECK_ALLOC(data.names);
-
- /** TODO: contains to be implemented */
-
- if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJ_COMMIT)) < 0)
- goto cleanup;
-
- if ((error = git_reference_foreach_name(
- git_object_owner(committish),
- get_name, &data)) < 0)
- goto cleanup;
-
- if (git_oidmap_size(data.names) == 0 && !opts->show_commit_oid_as_fallback) {
- giterr_set(GITERR_DESCRIBE, "Cannot describe - "
- "No reference found, cannot describe anything.");
- error = -1;
- goto cleanup;
- }
-
- if ((error = describe(&data, commit)) < 0)
- goto cleanup;
-
-cleanup:
- git_commit_free(commit);
-
- git_oidmap_foreach_value(data.names, name, {
- git_tag_free(name->tag);
- git__free(name->path);
- git__free(name);
- });
-
- git_oidmap_free(data.names);
-
- if (error < 0)
- git_describe_result_free(data.result);
- else
- *result = data.result;
-
- return error;
-}
-
-int git_describe_workdir(
- git_describe_result **out,
- git_repository *repo,
- git_describe_options *opts)
-{
- int error;
- git_oid current_id;
- git_status_list *status = NULL;
- git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
- git_describe_result *result = NULL;
- git_object *commit;
-
- if ((error = git_reference_name_to_id(¤t_id, repo, GIT_HEAD_FILE)) < 0)
- return error;
-
- if ((error = git_object_lookup(&commit, repo, ¤t_id, GIT_OBJ_COMMIT)) < 0)
- return error;
-
- /* The first step is to perform a describe of HEAD, so we can leverage this */
- if ((error = git_describe_commit(&result, commit, opts)) < 0)
- goto out;
-
- if ((error = git_status_list_new(&status, repo, &status_opts)) < 0)
- goto out;
-
-
- if (git_status_list_entrycount(status) > 0)
- result->dirty = 1;
-
-out:
- git_object_free(commit);
- git_status_list_free(status);
-
- if (error < 0)
- git_describe_result_free(result);
- else
- *out = result;
-
- return error;
-}
-
-static int normalize_format_options(
- git_describe_format_options *dst,
- const git_describe_format_options *src)
-{
- if (!src) {
- git_describe_init_format_options(dst, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION);
- return 0;
- }
-
- memcpy(dst, src, sizeof(git_describe_format_options));
- return 0;
-}
-
-int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *given)
-{
- int error;
- git_repository *repo;
- struct commit_name *name;
- git_describe_format_options opts;
-
- assert(out && result);
-
- GITERR_CHECK_VERSION(given, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options");
- normalize_format_options(&opts, given);
-
- git_buf_sanitize(out);
-
-
- if (opts.always_use_long_format && opts.abbreviated_size == 0) {
- giterr_set(GITERR_DESCRIBE, "Cannot describe - "
- "'always_use_long_format' is incompatible with a zero"
- "'abbreviated_size'");
- return -1;
- }
-
-
- repo = result->repo;
-
- /* If we did find an exact match, then it's the easier method */
- if (result->exact_match) {
- name = result->name;
- if ((error = display_name(out, repo, name)) < 0)
- return error;
-
- if (opts.always_use_long_format) {
- const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id;
- if ((error = show_suffix(out, 0, repo, id, opts.abbreviated_size)) < 0)
- return error;
- }
-
- if (result->dirty && opts.dirty_suffix)
- git_buf_puts(out, opts.dirty_suffix);
-
- return git_buf_oom(out) ? -1 : 0;
- }
-
- /* If we didn't find *any* tags, we fall back to the commit's id */
- if (result->fallback_to_id) {
- char hex_oid[GIT_OID_HEXSZ + 1] = {0};
- int size = 0;
-
- if ((error = find_unique_abbrev_size(
- &size, repo, &result->commit_id, opts.abbreviated_size)) < 0)
- return -1;
-
- git_oid_fmt(hex_oid, &result->commit_id);
- git_buf_put(out, hex_oid, size);
-
- if (result->dirty && opts.dirty_suffix)
- git_buf_puts(out, opts.dirty_suffix);
-
- return git_buf_oom(out) ? -1 : 0;
- }
-
- /* Lastly, if we found a matching tag, we show that */
- name = result->tag->name;
-
- if ((error = display_name(out, repo, name)) < 0)
- return error;
-
- if (opts.abbreviated_size) {
- if ((error = show_suffix(out, result->tag->depth, repo,
- &result->commit_id, opts.abbreviated_size)) < 0)
- return error;
- }
-
- if (result->dirty && opts.dirty_suffix) {
- git_buf_puts(out, opts.dirty_suffix);
- }
-
- return git_buf_oom(out) ? -1 : 0;
-}
-
-void git_describe_result_free(git_describe_result *result)
-{
- if (result == NULL)
- return;
-
- if (result->name) {
- git_tag_free(result->name->tag);
- git__free(result->name->path);
- git__free(result->name);
- }
-
- if (result->tag) {
- git_tag_free(result->tag->name->tag);
- git__free(result->tag->name->path);
- git__free(result->tag->name);
- git__free(result->tag);
- }
-
- git__free(result);
-}
-
-int git_describe_init_options(git_describe_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_describe_options, GIT_DESCRIBE_OPTIONS_INIT);
- return 0;
-}
-
-int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_describe_format_options, GIT_DESCRIBE_FORMAT_OPTIONS_INIT);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "git2/version.h"
-#include "common.h"
-#include "diff.h"
-#include "diff_generate.h"
-#include "patch.h"
-#include "commit.h"
-#include "index.h"
-
-#define DIFF_FLAG_IS_SET(DIFF,FLAG) \
- (((DIFF)->opts.flags & (FLAG)) != 0)
-#define DIFF_FLAG_ISNT_SET(DIFF,FLAG) \
- (((DIFF)->opts.flags & (FLAG)) == 0)
-#define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->opts.flags = \
- (VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL))
-
-GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta)
-{
- const char *str = delta->old_file.path;
-
- if (!str ||
- delta->status == GIT_DELTA_ADDED ||
- delta->status == GIT_DELTA_RENAMED ||
- delta->status == GIT_DELTA_COPIED)
- str = delta->new_file.path;
-
- return str;
-}
-
-const char *git_diff_delta__path(const git_diff_delta *delta)
-{
- return diff_delta__path(delta);
-}
-
-int git_diff_delta__cmp(const void *a, const void *b)
-{
- const git_diff_delta *da = a, *db = b;
- int val = strcmp(diff_delta__path(da), diff_delta__path(db));
- return val ? val : ((int)da->status - (int)db->status);
-}
-
-int git_diff_delta__casecmp(const void *a, const void *b)
-{
- const git_diff_delta *da = a, *db = b;
- int val = strcasecmp(diff_delta__path(da), diff_delta__path(db));
- return val ? val : ((int)da->status - (int)db->status);
-}
-
-int git_diff__entry_cmp(const void *a, const void *b)
-{
- const git_index_entry *entry_a = a;
- const git_index_entry *entry_b = b;
-
- return strcmp(entry_a->path, entry_b->path);
-}
-
-int git_diff__entry_icmp(const void *a, const void *b)
-{
- const git_index_entry *entry_a = a;
- const git_index_entry *entry_b = b;
-
- return strcasecmp(entry_a->path, entry_b->path);
-}
-
-void git_diff_free(git_diff *diff)
-{
- if (!diff)
- return;
-
- GIT_REFCOUNT_DEC(diff, diff->free_fn);
-}
-
-void git_diff_addref(git_diff *diff)
-{
- GIT_REFCOUNT_INC(diff);
-}
-
-size_t git_diff_num_deltas(const git_diff *diff)
-{
- assert(diff);
- return diff->deltas.length;
-}
-
-size_t git_diff_num_deltas_of_type(const git_diff *diff, git_delta_t type)
-{
- size_t i, count = 0;
- const git_diff_delta *delta;
-
- assert(diff);
-
- git_vector_foreach(&diff->deltas, i, delta) {
- count += (delta->status == type);
- }
-
- return count;
-}
-
-const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx)
-{
- assert(diff);
- return git_vector_get(&diff->deltas, idx);
-}
-
-int git_diff_is_sorted_icase(const git_diff *diff)
-{
- return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
-}
-
-int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff)
-{
- assert(out);
- GITERR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
- out->stat_calls = diff->perf.stat_calls;
- out->oid_calculations = diff->perf.oid_calculations;
- return 0;
-}
-
-int git_diff_format_email__append_header_tobuf(
- git_buf *out,
- const git_oid *id,
- const git_signature *author,
- const char *summary,
- const char *body,
- size_t patch_no,
- size_t total_patches,
- bool exclude_patchno_marker)
-{
- char idstr[GIT_OID_HEXSZ + 1];
- char date_str[GIT_DATE_RFC2822_SZ];
- int error = 0;
-
- git_oid_fmt(idstr, id);
- idstr[GIT_OID_HEXSZ] = '\0';
-
- if ((error = git__date_rfc2822_fmt(date_str, sizeof(date_str),
- &author->when)) < 0)
- return error;
-
- error = git_buf_printf(out,
- "From %s Mon Sep 17 00:00:00 2001\n" \
- "From: %s <%s>\n" \
- "Date: %s\n" \
- "Subject: ",
- idstr,
- author->name, author->email,
- date_str);
-
- if (error < 0)
- return error;
-
- if (!exclude_patchno_marker) {
- if (total_patches == 1) {
- error = git_buf_puts(out, "[PATCH] ");
- } else {
- error = git_buf_printf(out, "[PATCH %"PRIuZ"/%"PRIuZ"] ",
- patch_no, total_patches);
- }
-
- if (error < 0)
- return error;
- }
-
- error = git_buf_printf(out, "%s\n\n", summary);
-
- if (body) {
- git_buf_puts(out, body);
-
- if (out->ptr[out->size - 1] != '\n')
- git_buf_putc(out, '\n');
- }
-
- return error;
-}
-
-int git_diff_format_email__append_patches_tobuf(
- git_buf *out,
- git_diff *diff)
-{
- size_t i, deltas;
- int error = 0;
-
- deltas = git_diff_num_deltas(diff);
-
- for (i = 0; i < deltas; ++i) {
- git_patch *patch = NULL;
-
- if ((error = git_patch_from_diff(&patch, diff, i)) >= 0)
- error = git_patch_to_buf(out, patch);
-
- git_patch_free(patch);
-
- if (error < 0)
- break;
- }
-
- return error;
-}
-
-int git_diff_format_email(
- git_buf *out,
- git_diff *diff,
- const git_diff_format_email_options *opts)
-{
- git_diff_stats *stats = NULL;
- char *summary = NULL, *loc = NULL;
- bool ignore_marker;
- unsigned int format_flags = 0;
- size_t allocsize;
- int error;
-
- assert(out && diff && opts);
- assert(opts->summary && opts->id && opts->author);
-
- GITERR_CHECK_VERSION(opts,
- GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION,
- "git_format_email_options");
-
- ignore_marker = (opts->flags &
- GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0;
-
- if (!ignore_marker) {
- if (opts->patch_no > opts->total_patches) {
- giterr_set(GITERR_INVALID,
- "patch %"PRIuZ" out of range. max %"PRIuZ,
- opts->patch_no, opts->total_patches);
- return -1;
- }
-
- if (opts->patch_no == 0) {
- giterr_set(GITERR_INVALID,
- "invalid patch no %"PRIuZ". should be >0", opts->patch_no);
- return -1;
- }
- }
-
- /* the summary we receive may not be clean.
- * it could potentially contain new line characters
- * or not be set, sanitize, */
- if ((loc = strpbrk(opts->summary, "\r\n")) != NULL) {
- size_t offset = 0;
-
- if ((offset = (loc - opts->summary)) == 0) {
- giterr_set(GITERR_INVALID, "summary is empty");
- error = -1;
- goto on_error;
- }
-
- GITERR_CHECK_ALLOC_ADD(&allocsize, offset, 1);
- summary = git__calloc(allocsize, sizeof(char));
- GITERR_CHECK_ALLOC(summary);
-
- strncpy(summary, opts->summary, offset);
- }
-
- error = git_diff_format_email__append_header_tobuf(out,
- opts->id, opts->author, summary == NULL ? opts->summary : summary,
- opts->body, opts->patch_no, opts->total_patches, ignore_marker);
-
- if (error < 0)
- goto on_error;
-
- format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY;
-
- if ((error = git_buf_puts(out, "---\n")) < 0 ||
- (error = git_diff_get_stats(&stats, diff)) < 0 ||
- (error = git_diff_stats_to_buf(out, stats, format_flags, 0)) < 0 ||
- (error = git_buf_putc(out, '\n')) < 0 ||
- (error = git_diff_format_email__append_patches_tobuf(out, diff)) < 0)
- goto on_error;
-
- error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n");
-
-on_error:
- git__free(summary);
- git_diff_stats_free(stats);
-
- return error;
-}
-
-int git_diff_commit_as_email(
- git_buf *out,
- git_repository *repo,
- git_commit *commit,
- size_t patch_no,
- size_t total_patches,
- git_diff_format_email_flags_t flags,
- const git_diff_options *diff_opts)
-{
- git_diff *diff = NULL;
- git_diff_format_email_options opts =
- GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
- int error;
-
- assert (out && repo && commit);
-
- opts.flags = flags;
- opts.patch_no = patch_no;
- opts.total_patches = total_patches;
- opts.id = git_commit_id(commit);
- opts.summary = git_commit_summary(commit);
- opts.body = git_commit_body(commit);
- opts.author = git_commit_author(commit);
-
- if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
- return error;
-
- error = git_diff_format_email(out, diff, &opts);
-
- git_diff_free(diff);
- return error;
-}
-
-int git_diff_init_options(git_diff_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT);
- return 0;
-}
-
-int git_diff_find_init_options(
- git_diff_find_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT);
- return 0;
-}
-
-int git_diff_format_email_init_options(
- git_diff_format_email_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_diff_format_email_options,
- GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT);
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_diff_h__
-#define INCLUDE_diff_h__
-
-#include "git2/diff.h"
-#include "git2/patch.h"
-#include "git2/sys/diff.h"
-#include "git2/oid.h"
-
-#include <stdio.h>
-#include "vector.h"
-#include "buffer.h"
-#include "iterator.h"
-#include "repository.h"
-#include "pool.h"
-#include "odb.h"
-
-#define DIFF_OLD_PREFIX_DEFAULT "a/"
-#define DIFF_NEW_PREFIX_DEFAULT "b/"
-
-typedef enum {
- GIT_DIFF_TYPE_UNKNOWN = 0,
- GIT_DIFF_TYPE_GENERATED = 1,
- GIT_DIFF_TYPE_PARSED = 2,
-} git_diff_origin_t;
-
-struct git_diff {
- git_refcount rc;
- git_repository *repo;
- git_diff_origin_t type;
- git_diff_options opts;
- git_vector deltas; /* vector of git_diff_delta */
- git_pool pool;
- git_iterator_type_t old_src;
- git_iterator_type_t new_src;
- git_diff_perfdata perf;
-
- int (*strcomp)(const char *, const char *);
- int (*strncomp)(const char *, const char *, size_t);
- int (*pfxcomp)(const char *str, const char *pfx);
- int (*entrycomp)(const void *a, const void *b);
-
- int (*patch_fn)(git_patch **out, git_diff *diff, size_t idx);
- void (*free_fn)(git_diff *diff);
-};
-
-extern int git_diff_delta__format_file_header(
- git_buf *out,
- const git_diff_delta *delta,
- const char *oldpfx,
- const char *newpfx,
- int oid_strlen);
-
-extern int git_diff_delta__cmp(const void *a, const void *b);
-extern int git_diff_delta__casecmp(const void *a, const void *b);
-
-extern int git_diff__entry_cmp(const void *a, const void *b);
-extern int git_diff__entry_icmp(const void *a, const void *b);
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-
-#include "git2/attr.h"
-
-#include "diff.h"
-#include "diff_driver.h"
-#include "strmap.h"
-#include "map.h"
-#include "buf_text.h"
-#include "config.h"
-#include "repository.h"
-
-GIT__USE_STRMAP
-
-typedef enum {
- DIFF_DRIVER_AUTO = 0,
- DIFF_DRIVER_BINARY = 1,
- DIFF_DRIVER_TEXT = 2,
- DIFF_DRIVER_PATTERNLIST = 3,
-} git_diff_driver_t;
-
-typedef struct {
- regex_t re;
- int flags;
-} git_diff_driver_pattern;
-
-enum {
- REG_NEGATE = (1 << 15) /* get out of the way of existing flags */
-};
-
-/* data for finding function context for a given file type */
-struct git_diff_driver {
- git_diff_driver_t type;
- uint32_t binary_flags;
- uint32_t other_flags;
- git_array_t(git_diff_driver_pattern) fn_patterns;
- regex_t word_pattern;
- char name[GIT_FLEX_ARRAY];
-};
-
-#include "userdiff.h"
-
-struct git_diff_driver_registry {
- git_strmap *drivers;
-};
-
-#define FORCE_DIFFABLE (GIT_DIFF_FORCE_TEXT | GIT_DIFF_FORCE_BINARY)
-
-static git_diff_driver global_drivers[3] = {
- { DIFF_DRIVER_AUTO, 0, 0, },
- { DIFF_DRIVER_BINARY, GIT_DIFF_FORCE_BINARY, 0 },
- { DIFF_DRIVER_TEXT, GIT_DIFF_FORCE_TEXT, 0 },
-};
-
-git_diff_driver_registry *git_diff_driver_registry_new(void)
-{
- git_diff_driver_registry *reg =
- git__calloc(1, sizeof(git_diff_driver_registry));
- if (!reg)
- return NULL;
-
- if (git_strmap_alloc(®->drivers) < 0) {
- git_diff_driver_registry_free(reg);
- return NULL;
- }
-
- return reg;
-}
-
-void git_diff_driver_registry_free(git_diff_driver_registry *reg)
-{
- git_diff_driver *drv;
-
- if (!reg)
- return;
-
- git_strmap_foreach_value(reg->drivers, drv, git_diff_driver_free(drv));
- git_strmap_free(reg->drivers);
- git__free(reg);
-}
-
-static int diff_driver_add_patterns(
- git_diff_driver *drv, const char *regex_str, int regex_flags)
-{
- int error = 0;
- const char *scan, *end;
- git_diff_driver_pattern *pat = NULL;
- git_buf buf = GIT_BUF_INIT;
-
- for (scan = regex_str; scan; scan = end) {
- /* get pattern to fill in */
- if ((pat = git_array_alloc(drv->fn_patterns)) == NULL) {
- return -1;
- }
-
- pat->flags = regex_flags;
- if (*scan == '!') {
- pat->flags |= REG_NEGATE;
- ++scan;
- }
-
- if ((end = strchr(scan, '\n')) != NULL) {
- error = git_buf_set(&buf, scan, end - scan);
- end++;
- } else {
- error = git_buf_sets(&buf, scan);
- }
- if (error < 0)
- break;
-
- if ((error = p_regcomp(&pat->re, buf.ptr, regex_flags)) != 0) {
- /*
- * TODO: issue a warning
- */
- }
- }
-
- if (error && pat != NULL)
- (void)git_array_pop(drv->fn_patterns); /* release last item */
- git_buf_free(&buf);
-
- /* We want to ignore bad patterns, so return success regardless */
- return 0;
-}
-
-static int diff_driver_xfuncname(const git_config_entry *entry, void *payload)
-{
- return diff_driver_add_patterns(payload, entry->value, REG_EXTENDED);
-}
-
-static int diff_driver_funcname(const git_config_entry *entry, void *payload)
-{
- return diff_driver_add_patterns(payload, entry->value, 0);
-}
-
-static git_diff_driver_registry *git_repository_driver_registry(
- git_repository *repo)
-{
- if (!repo->diff_drivers) {
- git_diff_driver_registry *reg = git_diff_driver_registry_new();
- reg = git__compare_and_swap(&repo->diff_drivers, NULL, reg);
-
- if (reg != NULL) /* if we race, free losing allocation */
- git_diff_driver_registry_free(reg);
- }
-
- if (!repo->diff_drivers)
- giterr_set(GITERR_REPOSITORY, "Unable to create diff driver registry");
-
- return repo->diff_drivers;
-}
-
-static int diff_driver_alloc(
- git_diff_driver **out, size_t *namelen_out, const char *name)
-{
- git_diff_driver *driver;
- size_t driverlen = sizeof(git_diff_driver),
- namelen = strlen(name),
- alloclen;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, driverlen, namelen);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
-
- driver = git__calloc(1, alloclen);
- GITERR_CHECK_ALLOC(driver);
-
- memcpy(driver->name, name, namelen);
-
- *out = driver;
-
- if (namelen_out)
- *namelen_out = namelen;
-
- return 0;
-}
-
-static int git_diff_driver_builtin(
- git_diff_driver **out,
- git_diff_driver_registry *reg,
- const char *driver_name)
-{
- int error = 0;
- git_diff_driver_definition *ddef = NULL;
- git_diff_driver *drv = NULL;
- size_t idx;
-
- for (idx = 0; idx < ARRAY_SIZE(builtin_defs); ++idx) {
- if (!strcasecmp(driver_name, builtin_defs[idx].name)) {
- ddef = &builtin_defs[idx];
- break;
- }
- }
- if (!ddef)
- goto done;
-
- if ((error = diff_driver_alloc(&drv, NULL, ddef->name)) < 0)
- goto done;
-
- drv->type = DIFF_DRIVER_PATTERNLIST;
-
- if (ddef->fns &&
- (error = diff_driver_add_patterns(
- drv, ddef->fns, ddef->flags | REG_EXTENDED)) < 0)
- goto done;
-
- if (ddef->words &&
- (error = p_regcomp(
- &drv->word_pattern, ddef->words, ddef->flags | REG_EXTENDED)))
- {
- error = giterr_set_regex(&drv->word_pattern, error);
- goto done;
- }
-
- git_strmap_insert(reg->drivers, drv->name, drv, error);
- if (error > 0)
- error = 0;
-
-done:
- if (error && drv)
- git_diff_driver_free(drv);
- else
- *out = drv;
-
- return error;
-}
-
-static int git_diff_driver_load(
- git_diff_driver **out, git_repository *repo, const char *driver_name)
-{
- int error = 0;
- git_diff_driver_registry *reg;
- git_diff_driver *drv = NULL;
- size_t namelen;
- khiter_t pos;
- git_config *cfg = NULL;
- git_buf name = GIT_BUF_INIT;
- git_config_entry *ce = NULL;
- bool found_driver = false;
-
- if ((reg = git_repository_driver_registry(repo)) == NULL)
- return -1;
-
- pos = git_strmap_lookup_index(reg->drivers, driver_name);
- if (git_strmap_valid_index(reg->drivers, pos)) {
- *out = git_strmap_value_at(reg->drivers, pos);
- return 0;
- }
-
- if ((error = diff_driver_alloc(&drv, &namelen, driver_name)) < 0)
- goto done;
-
- drv->type = DIFF_DRIVER_AUTO;
-
- /* if you can't read config for repo, just use default driver */
- if (git_repository_config_snapshot(&cfg, repo) < 0) {
- giterr_clear();
- goto done;
- }
-
- if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0)
- goto done;
-
- switch (git_config__get_bool_force(cfg, name.ptr, -1)) {
- case true:
- /* if diff.<driver>.binary is true, just return the binary driver */
- *out = &global_drivers[DIFF_DRIVER_BINARY];
- goto done;
- case false:
- /* if diff.<driver>.binary is false, force binary checks off */
- /* but still may have custom function context patterns, etc. */
- drv->binary_flags = GIT_DIFF_FORCE_TEXT;
- found_driver = true;
- break;
- default:
- /* diff.<driver>.binary unspecified or "auto", so just continue */
- break;
- }
-
- /* TODO: warn if diff.<name>.command or diff.<name>.textconv are set */
-
- git_buf_truncate(&name, namelen + strlen("diff.."));
- git_buf_put(&name, "xfuncname", strlen("xfuncname"));
- if ((error = git_config_get_multivar_foreach(
- cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) {
- if (error != GIT_ENOTFOUND)
- goto done;
- giterr_clear(); /* no diff.<driver>.xfuncname, so just continue */
- }
-
- git_buf_truncate(&name, namelen + strlen("diff.."));
- git_buf_put(&name, "funcname", strlen("funcname"));
- if ((error = git_config_get_multivar_foreach(
- cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) {
- if (error != GIT_ENOTFOUND)
- goto done;
- giterr_clear(); /* no diff.<driver>.funcname, so just continue */
- }
-
- /* if we found any patterns, set driver type to use correct callback */
- if (git_array_size(drv->fn_patterns) > 0) {
- drv->type = DIFF_DRIVER_PATTERNLIST;
- found_driver = true;
- }
-
- git_buf_truncate(&name, namelen + strlen("diff.."));
- git_buf_put(&name, "wordregex", strlen("wordregex"));
- if ((error = git_config__lookup_entry(&ce, cfg, name.ptr, false)) < 0)
- goto done;
- if (!ce || !ce->value)
- /* no diff.<driver>.wordregex, so just continue */;
- else if (!(error = p_regcomp(&drv->word_pattern, ce->value, REG_EXTENDED)))
- found_driver = true;
- else {
- /* TODO: warn about bad regex instead of failure */
- error = giterr_set_regex(&drv->word_pattern, error);
- goto done;
- }
-
- /* TODO: look up diff.<driver>.algorithm to turn on minimal / patience
- * diff in drv->other_flags
- */
-
- /* if no driver config found at all, fall back on AUTO driver */
- if (!found_driver)
- goto done;
-
- /* store driver in registry */
- git_strmap_insert(reg->drivers, drv->name, drv, error);
- if (error < 0)
- goto done;
- error = 0;
-
- *out = drv;
-
-done:
- git_config_entry_free(ce);
- git_buf_free(&name);
- git_config_free(cfg);
-
- if (!*out) {
- int error2 = git_diff_driver_builtin(out, reg, driver_name);
- if (!error)
- error = error2;
- }
-
- if (drv && drv != *out)
- git_diff_driver_free(drv);
-
- return error;
-}
-
-int git_diff_driver_lookup(
- git_diff_driver **out, git_repository *repo, const char *path)
-{
- int error = 0;
- const char *value;
-
- assert(out);
- *out = NULL;
-
- if (!repo || !path || !strlen(path))
- /* just use the auto value */;
- else if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0)
- /* return error below */;
- else if (GIT_ATTR_UNSPECIFIED(value))
- /* just use the auto value */;
- else if (GIT_ATTR_FALSE(value))
- *out = &global_drivers[DIFF_DRIVER_BINARY];
- else if (GIT_ATTR_TRUE(value))
- *out = &global_drivers[DIFF_DRIVER_TEXT];
-
- /* otherwise look for driver information in config and build driver */
- else if ((error = git_diff_driver_load(out, repo, value)) < 0) {
- if (error == GIT_ENOTFOUND) {
- error = 0;
- giterr_clear();
- }
- }
-
- if (!*out)
- *out = &global_drivers[DIFF_DRIVER_AUTO];
-
- return error;
-}
-
-void git_diff_driver_free(git_diff_driver *driver)
-{
- size_t i;
-
- if (!driver)
- return;
-
- for (i = 0; i < git_array_size(driver->fn_patterns); ++i)
- regfree(& git_array_get(driver->fn_patterns, i)->re);
- git_array_clear(driver->fn_patterns);
-
- regfree(&driver->word_pattern);
-
- git__free(driver);
-}
-
-void git_diff_driver_update_options(
- uint32_t *option_flags, git_diff_driver *driver)
-{
- if ((*option_flags & FORCE_DIFFABLE) == 0)
- *option_flags |= driver->binary_flags;
-
- *option_flags |= driver->other_flags;
-}
-
-int git_diff_driver_content_is_binary(
- git_diff_driver *driver, const char *content, size_t content_len)
-{
- git_buf search = GIT_BUF_INIT;
-
- GIT_UNUSED(driver);
-
- git_buf_attach_notowned(&search, content,
- min(content_len, GIT_FILTER_BYTES_TO_CHECK_NUL));
-
- /* TODO: provide encoding / binary detection callbacks that can
- * be UTF-8 aware, etc. For now, instead of trying to be smart,
- * let's just use the simple NUL-byte detection that core git uses.
- */
-
- /* previously was: if (git_buf_text_is_binary(&search)) */
- if (git_buf_text_contains_nul(&search))
- return 1;
-
- return 0;
-}
-
-static int diff_context_line__simple(
- git_diff_driver *driver, git_buf *line)
-{
- char firstch = line->ptr[0];
- GIT_UNUSED(driver);
- return (git__isalpha(firstch) || firstch == '_' || firstch == '$');
-}
-
-static int diff_context_line__pattern_match(
- git_diff_driver *driver, git_buf *line)
-{
- size_t i, maxi = git_array_size(driver->fn_patterns);
- regmatch_t pmatch[2];
-
- for (i = 0; i < maxi; ++i) {
- git_diff_driver_pattern *pat = git_array_get(driver->fn_patterns, i);
-
- if (!regexec(&pat->re, line->ptr, 2, pmatch, 0)) {
- if (pat->flags & REG_NEGATE)
- return false;
-
- /* use pmatch data to trim line data */
- i = (pmatch[1].rm_so >= 0) ? 1 : 0;
- git_buf_consume(line, git_buf_cstr(line) + pmatch[i].rm_so);
- git_buf_truncate(line, pmatch[i].rm_eo - pmatch[i].rm_so);
- git_buf_rtrim(line);
-
- return true;
- }
- }
-
- return false;
-}
-
-static long diff_context_find(
- const char *line,
- long line_len,
- char *out,
- long out_size,
- void *payload)
-{
- git_diff_find_context_payload *ctxt = payload;
-
- if (git_buf_set(&ctxt->line, line, (size_t)line_len) < 0)
- return -1;
- git_buf_rtrim(&ctxt->line);
-
- if (!ctxt->line.size)
- return -1;
-
- if (!ctxt->match_line || !ctxt->match_line(ctxt->driver, &ctxt->line))
- return -1;
-
- if (out_size > (long)ctxt->line.size)
- out_size = (long)ctxt->line.size;
- memcpy(out, ctxt->line.ptr, (size_t)out_size);
-
- return out_size;
-}
-
-void git_diff_find_context_init(
- git_diff_find_context_fn *findfn_out,
- git_diff_find_context_payload *payload_out,
- git_diff_driver *driver)
-{
- *findfn_out = driver ? diff_context_find : NULL;
-
- memset(payload_out, 0, sizeof(*payload_out));
- if (driver) {
- payload_out->driver = driver;
- payload_out->match_line = (driver->type == DIFF_DRIVER_PATTERNLIST) ?
- diff_context_line__pattern_match : diff_context_line__simple;
- git_buf_init(&payload_out->line, 0);
- }
-}
-
-void git_diff_find_context_clear(git_diff_find_context_payload *payload)
-{
- if (payload) {
- git_buf_free(&payload->line);
- payload->driver = NULL;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_diff_driver_h__
-#define INCLUDE_diff_driver_h__
-
-#include "common.h"
-#include "buffer.h"
-
-typedef struct git_diff_driver_registry git_diff_driver_registry;
-
-git_diff_driver_registry *git_diff_driver_registry_new(void);
-void git_diff_driver_registry_free(git_diff_driver_registry *);
-
-typedef struct git_diff_driver git_diff_driver;
-
-int git_diff_driver_lookup(git_diff_driver **, git_repository *, const char *);
-void git_diff_driver_free(git_diff_driver *);
-
-/* diff option flags to force off and on for this driver */
-void git_diff_driver_update_options(uint32_t *option_flags, git_diff_driver *);
-
-/* returns -1 meaning "unknown", 0 meaning not binary, 1 meaning binary */
-int git_diff_driver_content_is_binary(
- git_diff_driver *, const char *content, size_t content_len);
-
-typedef long (*git_diff_find_context_fn)(
- const char *, long, char *, long, void *);
-
-typedef int (*git_diff_find_context_line)(
- git_diff_driver *, git_buf *);
-
-typedef struct {
- git_diff_driver *driver;
- git_diff_find_context_line match_line;
- git_buf line;
-} git_diff_find_context_payload;
-
-void git_diff_find_context_init(
- git_diff_find_context_fn *findfn_out,
- git_diff_find_context_payload *payload_out,
- git_diff_driver *driver);
-
-void git_diff_find_context_clear(git_diff_find_context_payload *);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "git2/blob.h"
-#include "git2/submodule.h"
-#include "diff.h"
-#include "diff_generate.h"
-#include "diff_file.h"
-#include "odb.h"
-#include "fileops.h"
-#include "filter.h"
-
-#define DIFF_MAX_FILESIZE 0x20000000
-
-static bool diff_file_content_binary_by_size(git_diff_file_content *fc)
-{
- /* if we have diff opts, check max_size vs file size */
- if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) == 0 &&
- fc->opts_max_size > 0 &&
- fc->file->size > fc->opts_max_size)
- fc->file->flags |= GIT_DIFF_FLAG_BINARY;
-
- return ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0);
-}
-
-static void diff_file_content_binary_by_content(git_diff_file_content *fc)
-{
- if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
- return;
-
- switch (git_diff_driver_content_is_binary(
- fc->driver, fc->map.data, fc->map.len)) {
- case 0: fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY; break;
- case 1: fc->file->flags |= GIT_DIFF_FLAG_BINARY; break;
- default: break;
- }
-}
-
-static int diff_file_content_init_common(
- git_diff_file_content *fc, const git_diff_options *opts)
-{
- fc->opts_flags = opts ? opts->flags : GIT_DIFF_NORMAL;
-
- if (opts && opts->max_size >= 0)
- fc->opts_max_size = opts->max_size ?
- opts->max_size : DIFF_MAX_FILESIZE;
-
- if (fc->src == GIT_ITERATOR_TYPE_EMPTY)
- fc->src = GIT_ITERATOR_TYPE_TREE;
-
- if (!fc->driver &&
- git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0)
- return -1;
-
- /* give driver a chance to modify options */
- git_diff_driver_update_options(&fc->opts_flags, fc->driver);
-
- /* make sure file is conceivable mmap-able */
- if ((git_off_t)((size_t)fc->file->size) != fc->file->size)
- fc->file->flags |= GIT_DIFF_FLAG_BINARY;
- /* check if user is forcing text diff the file */
- else if (fc->opts_flags & GIT_DIFF_FORCE_TEXT) {
- fc->file->flags &= ~GIT_DIFF_FLAG_BINARY;
- fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY;
- }
- /* check if user is forcing binary diff the file */
- else if (fc->opts_flags & GIT_DIFF_FORCE_BINARY) {
- fc->file->flags &= ~GIT_DIFF_FLAG_NOT_BINARY;
- fc->file->flags |= GIT_DIFF_FLAG_BINARY;
- }
-
- diff_file_content_binary_by_size(fc);
-
- if ((fc->flags & GIT_DIFF_FLAG__NO_DATA) != 0) {
- fc->flags |= GIT_DIFF_FLAG__LOADED;
- fc->map.len = 0;
- fc->map.data = "";
- }
-
- if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0)
- diff_file_content_binary_by_content(fc);
-
- return 0;
-}
-
-int git_diff_file_content__init_from_diff(
- git_diff_file_content *fc,
- git_diff *diff,
- git_diff_delta *delta,
- bool use_old)
-{
- bool has_data = true;
-
- memset(fc, 0, sizeof(*fc));
- fc->repo = diff->repo;
- fc->file = use_old ? &delta->old_file : &delta->new_file;
- fc->src = use_old ? diff->old_src : diff->new_src;
-
- if (git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0)
- return -1;
-
- switch (delta->status) {
- case GIT_DELTA_ADDED:
- has_data = !use_old; break;
- case GIT_DELTA_DELETED:
- has_data = use_old; break;
- case GIT_DELTA_UNTRACKED:
- has_data = !use_old &&
- (diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
- break;
- case GIT_DELTA_UNREADABLE:
- case GIT_DELTA_MODIFIED:
- case GIT_DELTA_COPIED:
- case GIT_DELTA_RENAMED:
- break;
- default:
- has_data = false;
- break;
- }
-
- if (!has_data)
- fc->flags |= GIT_DIFF_FLAG__NO_DATA;
-
- return diff_file_content_init_common(fc, &diff->opts);
-}
-
-int git_diff_file_content__init_from_src(
- git_diff_file_content *fc,
- git_repository *repo,
- const git_diff_options *opts,
- const git_diff_file_content_src *src,
- git_diff_file *as_file)
-{
- memset(fc, 0, sizeof(*fc));
- fc->repo = repo;
- fc->file = as_file;
- fc->blob = src->blob;
-
- if (!src->blob && !src->buf) {
- fc->flags |= GIT_DIFF_FLAG__NO_DATA;
- } else {
- fc->flags |= GIT_DIFF_FLAG__LOADED;
- fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
- fc->file->mode = GIT_FILEMODE_BLOB;
-
- if (src->blob) {
- fc->file->size = git_blob_rawsize(src->blob);
- git_oid_cpy(&fc->file->id, git_blob_id(src->blob));
- fc->file->id_abbrev = GIT_OID_HEXSZ;
-
- fc->map.len = (size_t)fc->file->size;
- fc->map.data = (char *)git_blob_rawcontent(src->blob);
- } else {
- fc->file->size = src->buflen;
- git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB);
- fc->file->id_abbrev = GIT_OID_HEXSZ;
-
- fc->map.len = src->buflen;
- fc->map.data = (char *)src->buf;
- }
- }
-
- return diff_file_content_init_common(fc, opts);
-}
-
-static int diff_file_content_commit_to_str(
- git_diff_file_content *fc, bool check_status)
-{
- char oid[GIT_OID_HEXSZ+1];
- git_buf content = GIT_BUF_INIT;
- const char *status = "";
-
- if (check_status) {
- int error = 0;
- git_submodule *sm = NULL;
- unsigned int sm_status = 0;
- const git_oid *sm_head;
-
- if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0) {
- /* GIT_EEXISTS means a "submodule" that has not been git added */
- if (error == GIT_EEXISTS) {
- giterr_clear();
- error = 0;
- }
- return error;
- }
-
- if ((error = git_submodule_status(&sm_status, fc->repo, fc->file->path, GIT_SUBMODULE_IGNORE_UNSPECIFIED)) < 0) {
- git_submodule_free(sm);
- return error;
- }
-
- /* update OID if we didn't have it previously */
- if ((fc->file->flags & GIT_DIFF_FLAG_VALID_ID) == 0 &&
- ((sm_head = git_submodule_wd_id(sm)) != NULL ||
- (sm_head = git_submodule_head_id(sm)) != NULL))
- {
- git_oid_cpy(&fc->file->id, sm_head);
- fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
- }
-
- if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
- status = "-dirty";
-
- git_submodule_free(sm);
- }
-
- git_oid_tostr(oid, sizeof(oid), &fc->file->id);
- if (git_buf_printf(&content, "Subproject commit %s%s\n", oid, status) < 0)
- return -1;
-
- fc->map.len = git_buf_len(&content);
- fc->map.data = git_buf_detach(&content);
- fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
-
- return 0;
-}
-
-static int diff_file_content_load_blob(
- git_diff_file_content *fc,
- git_diff_options *opts)
-{
- int error = 0;
- git_odb_object *odb_obj = NULL;
-
- if (git_oid_iszero(&fc->file->id))
- return 0;
-
- if (fc->file->mode == GIT_FILEMODE_COMMIT)
- return diff_file_content_commit_to_str(fc, false);
-
- /* if we don't know size, try to peek at object header first */
- if (!fc->file->size) {
- if ((error = git_diff_file__resolve_zero_size(
- fc->file, &odb_obj, fc->repo)) < 0)
- return error;
- }
-
- if ((opts->flags & GIT_DIFF_SHOW_BINARY) == 0 &&
- diff_file_content_binary_by_size(fc))
- return 0;
-
- if (odb_obj != NULL) {
- error = git_object__from_odb_object(
- (git_object **)&fc->blob, fc->repo, odb_obj, GIT_OBJ_BLOB);
- git_odb_object_free(odb_obj);
- } else {
- error = git_blob_lookup(
- (git_blob **)&fc->blob, fc->repo, &fc->file->id);
- }
-
- if (!error) {
- fc->flags |= GIT_DIFF_FLAG__FREE_BLOB;
- fc->map.data = (void *)git_blob_rawcontent(fc->blob);
- fc->map.len = (size_t)git_blob_rawsize(fc->blob);
- }
-
- return error;
-}
-
-static int diff_file_content_load_workdir_symlink_fake(
- git_diff_file_content *fc, git_buf *path)
-{
- git_buf target = GIT_BUF_INIT;
- int error;
-
- if ((error = git_futils_readbuffer(&target, path->ptr)) < 0)
- return error;
-
- fc->map.len = git_buf_len(&target);
- fc->map.data = git_buf_detach(&target);
- fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
-
- git_buf_free(&target);
- return error;
-}
-
-static int diff_file_content_load_workdir_symlink(
- git_diff_file_content *fc, git_buf *path)
-{
- ssize_t alloc_len, read_len;
- int symlink_supported, error;
-
- if ((error = git_repository__cvar(
- &symlink_supported, fc->repo, GIT_CVAR_SYMLINKS)) < 0)
- return -1;
-
- if (!symlink_supported)
- return diff_file_content_load_workdir_symlink_fake(fc, path);
-
- /* link path on disk could be UTF-16, so prepare a buffer that is
- * big enough to handle some UTF-8 data expansion
- */
- alloc_len = (ssize_t)(fc->file->size * 2) + 1;
-
- fc->map.data = git__calloc(alloc_len, sizeof(char));
- GITERR_CHECK_ALLOC(fc->map.data);
-
- fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
-
- read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len);
- if (read_len < 0) {
- giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file->path);
- return -1;
- }
-
- fc->map.len = read_len;
- return 0;
-}
-
-static int diff_file_content_load_workdir_file(
- git_diff_file_content *fc,
- git_buf *path,
- git_diff_options *diff_opts)
-{
- int error = 0;
- git_filter_list *fl = NULL;
- git_file fd = git_futils_open_ro(git_buf_cstr(path));
- git_buf raw = GIT_BUF_INIT;
-
- if (fd < 0)
- return fd;
-
- if (!fc->file->size &&
- !(fc->file->size = git_futils_filesize(fd)))
- goto cleanup;
-
- if ((diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0 &&
- diff_file_content_binary_by_size(fc))
- goto cleanup;
-
- if ((error = git_filter_list_load(
- &fl, fc->repo, NULL, fc->file->path,
- GIT_FILTER_TO_ODB, GIT_FILTER_ALLOW_UNSAFE)) < 0)
- goto cleanup;
-
- /* if there are no filters, try to mmap the file */
- if (fl == NULL) {
- if (!(error = git_futils_mmap_ro(
- &fc->map, fd, 0, (size_t)fc->file->size))) {
- fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
- goto cleanup;
- }
-
- /* if mmap failed, fall through to try readbuffer below */
- giterr_clear();
- }
-
- if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size))) {
- git_buf out = GIT_BUF_INIT;
-
- error = git_filter_list_apply_to_data(&out, fl, &raw);
-
- if (out.ptr != raw.ptr)
- git_buf_free(&raw);
-
- if (!error) {
- fc->map.len = out.size;
- fc->map.data = out.ptr;
- fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
- }
- }
-
-cleanup:
- git_filter_list_free(fl);
- p_close(fd);
-
- return error;
-}
-
-static int diff_file_content_load_workdir(
- git_diff_file_content *fc,
- git_diff_options *diff_opts)
-{
- int error = 0;
- git_buf path = GIT_BUF_INIT;
-
- if (fc->file->mode == GIT_FILEMODE_COMMIT)
- return diff_file_content_commit_to_str(fc, true);
-
- if (fc->file->mode == GIT_FILEMODE_TREE)
- return 0;
-
- if (git_buf_joinpath(
- &path, git_repository_workdir(fc->repo), fc->file->path) < 0)
- return -1;
-
- if (S_ISLNK(fc->file->mode))
- error = diff_file_content_load_workdir_symlink(fc, &path);
- else
- error = diff_file_content_load_workdir_file(fc, &path, diff_opts);
-
- /* once data is loaded, update OID if we didn't have it previously */
- if (!error && (fc->file->flags & GIT_DIFF_FLAG_VALID_ID) == 0) {
- error = git_odb_hash(
- &fc->file->id, fc->map.data, fc->map.len, GIT_OBJ_BLOB);
- fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
- }
-
- git_buf_free(&path);
- return error;
-}
-
-int git_diff_file_content__load(
- git_diff_file_content *fc,
- git_diff_options *diff_opts)
-{
- int error = 0;
-
- if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0)
- return 0;
-
- if ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0 &&
- (diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0)
- return 0;
-
- if (fc->src == GIT_ITERATOR_TYPE_WORKDIR)
- error = diff_file_content_load_workdir(fc, diff_opts);
- else
- error = diff_file_content_load_blob(fc, diff_opts);
- if (error)
- return error;
-
- fc->flags |= GIT_DIFF_FLAG__LOADED;
-
- diff_file_content_binary_by_content(fc);
-
- return 0;
-}
-
-void git_diff_file_content__unload(git_diff_file_content *fc)
-{
- if ((fc->flags & GIT_DIFF_FLAG__LOADED) == 0)
- return;
-
- if (fc->flags & GIT_DIFF_FLAG__FREE_DATA) {
- git__free(fc->map.data);
- fc->map.data = "";
- fc->map.len = 0;
- fc->flags &= ~GIT_DIFF_FLAG__FREE_DATA;
- }
- else if (fc->flags & GIT_DIFF_FLAG__UNMAP_DATA) {
- git_futils_mmap_free(&fc->map);
- fc->map.data = "";
- fc->map.len = 0;
- fc->flags &= ~GIT_DIFF_FLAG__UNMAP_DATA;
- }
-
- if (fc->flags & GIT_DIFF_FLAG__FREE_BLOB) {
- git_blob_free((git_blob *)fc->blob);
- fc->blob = NULL;
- fc->flags &= ~GIT_DIFF_FLAG__FREE_BLOB;
- }
-
- fc->flags &= ~GIT_DIFF_FLAG__LOADED;
-}
-
-void git_diff_file_content__clear(git_diff_file_content *fc)
-{
- git_diff_file_content__unload(fc);
-
- /* for now, nothing else to do */
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_diff_file_h__
-#define INCLUDE_diff_file_h__
-
-#include "common.h"
-#include "diff.h"
-#include "diff_driver.h"
-#include "map.h"
-
-/* expanded information for one side of a delta */
-typedef struct {
- git_repository *repo;
- git_diff_file *file;
- git_diff_driver *driver;
- uint32_t flags;
- uint32_t opts_flags;
- git_off_t opts_max_size;
- git_iterator_type_t src;
- const git_blob *blob;
- git_map map;
-} git_diff_file_content;
-
-extern int git_diff_file_content__init_from_diff(
- git_diff_file_content *fc,
- git_diff *diff,
- git_diff_delta *delta,
- bool use_old);
-
-typedef struct {
- const git_blob *blob;
- const void *buf;
- size_t buflen;
- const char *as_path;
-} git_diff_file_content_src;
-
-#define GIT_DIFF_FILE_CONTENT_SRC__BLOB(BLOB,PATH) { (BLOB),NULL,0,(PATH) }
-#define GIT_DIFF_FILE_CONTENT_SRC__BUF(BUF,LEN,PATH) { NULL,(BUF),(LEN),(PATH) }
-
-extern int git_diff_file_content__init_from_src(
- git_diff_file_content *fc,
- git_repository *repo,
- const git_diff_options *opts,
- const git_diff_file_content_src *src,
- git_diff_file *as_file);
-
-/* this loads the blob/file-on-disk as needed */
-extern int git_diff_file_content__load(
- git_diff_file_content *fc,
- git_diff_options *diff_opts);
-
-/* this releases the blob/file-in-memory */
-extern void git_diff_file_content__unload(git_diff_file_content *fc);
-
-/* this unloads and also releases any other resources */
-extern void git_diff_file_content__clear(git_diff_file_content *fc);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "diff.h"
-#include "diff_generate.h"
-#include "patch_generate.h"
-#include "fileops.h"
-#include "config.h"
-#include "attr_file.h"
-#include "filter.h"
-#include "pathspec.h"
-#include "index.h"
-#include "odb.h"
-#include "submodule.h"
-
-#define DIFF_FLAG_IS_SET(DIFF,FLAG) \
- (((DIFF)->base.opts.flags & (FLAG)) != 0)
-#define DIFF_FLAG_ISNT_SET(DIFF,FLAG) \
- (((DIFF)->base.opts.flags & (FLAG)) == 0)
-#define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->base.opts.flags = \
- (VAL) ? ((DIFF)->base.opts.flags | (FLAG)) : \
- ((DIFF)->base.opts.flags & ~(VAL))
-
-typedef struct {
- struct git_diff base;
-
- git_vector pathspec;
-
- uint32_t diffcaps;
- bool index_updated;
-} git_diff_generated;
-
-static git_diff_delta *diff_delta__alloc(
- git_diff_generated *diff,
- git_delta_t status,
- const char *path)
-{
- git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
- if (!delta)
- return NULL;
-
- delta->old_file.path = git_pool_strdup(&diff->base.pool, path);
- if (delta->old_file.path == NULL) {
- git__free(delta);
- return NULL;
- }
-
- delta->new_file.path = delta->old_file.path;
-
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
- switch (status) {
- case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break;
- case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
- default: break; /* leave other status values alone */
- }
- }
- delta->status = status;
-
- return delta;
-}
-
-static int diff_insert_delta(
- git_diff_generated *diff,
- git_diff_delta *delta,
- const char *matched_pathspec)
-{
- int error = 0;
-
- if (diff->base.opts.notify_cb) {
- error = diff->base.opts.notify_cb(
- &diff->base, delta, matched_pathspec, diff->base.opts.payload);
-
- if (error) {
- git__free(delta);
-
- if (error > 0) /* positive value means to skip this delta */
- return 0;
- else /* negative value means to cancel diff */
- return giterr_set_after_callback_function(error, "git_diff");
- }
- }
-
- if ((error = git_vector_insert(&diff->base.deltas, delta)) < 0)
- git__free(delta);
-
- return error;
-}
-
-static bool diff_pathspec_match(
- const char **matched_pathspec,
- git_diff_generated *diff,
- const git_index_entry *entry)
-{
- bool disable_pathspec_match =
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH);
-
- /* If we're disabling fnmatch, then the iterator has already applied
- * the filters to the files for us and we don't have to do anything.
- * However, this only applies to *files* - the iterator will include
- * directories that we need to recurse into when not autoexpanding,
- * so we still need to apply the pathspec match to directories.
- */
- if ((S_ISLNK(entry->mode) || S_ISREG(entry->mode)) &&
- disable_pathspec_match) {
- *matched_pathspec = entry->path;
- return true;
- }
-
- return git_pathspec__match(
- &diff->pathspec, entry->path, disable_pathspec_match,
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
- matched_pathspec, NULL);
-}
-
-static int diff_delta__from_one(
- git_diff_generated *diff,
- git_delta_t status,
- const git_index_entry *oitem,
- const git_index_entry *nitem)
-{
- const git_index_entry *entry = nitem;
- bool has_old = false;
- git_diff_delta *delta;
- const char *matched_pathspec;
-
- assert((oitem != NULL) ^ (nitem != NULL));
-
- if (oitem) {
- entry = oitem;
- has_old = true;
- }
-
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE))
- has_old = !has_old;
-
- if ((entry->flags & GIT_IDXENTRY_VALID) != 0)
- return 0;
-
- if (status == GIT_DELTA_IGNORED &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED))
- return 0;
-
- if (status == GIT_DELTA_UNTRACKED &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
- return 0;
-
- if (status == GIT_DELTA_UNREADABLE &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE))
- return 0;
-
- if (!diff_pathspec_match(&matched_pathspec, diff, entry))
- return 0;
-
- delta = diff_delta__alloc(diff, status, entry->path);
- GITERR_CHECK_ALLOC(delta);
-
- /* This fn is just for single-sided diffs */
- assert(status != GIT_DELTA_MODIFIED);
- delta->nfiles = 1;
-
- if (has_old) {
- delta->old_file.mode = entry->mode;
- delta->old_file.size = entry->file_size;
- delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
- git_oid_cpy(&delta->old_file.id, &entry->id);
- delta->old_file.id_abbrev = GIT_OID_HEXSZ;
- } else /* ADDED, IGNORED, UNTRACKED */ {
- delta->new_file.mode = entry->mode;
- delta->new_file.size = entry->file_size;
- delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
- git_oid_cpy(&delta->new_file.id, &entry->id);
- delta->new_file.id_abbrev = GIT_OID_HEXSZ;
- }
-
- delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
-
- if (has_old || !git_oid_iszero(&delta->new_file.id))
- delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
-
- return diff_insert_delta(diff, delta, matched_pathspec);
-}
-
-static int diff_delta__from_two(
- git_diff_generated *diff,
- git_delta_t status,
- const git_index_entry *old_entry,
- uint32_t old_mode,
- const git_index_entry *new_entry,
- uint32_t new_mode,
- const git_oid *new_id,
- const char *matched_pathspec)
-{
- const git_oid *old_id = &old_entry->id;
- git_diff_delta *delta;
- const char *canonical_path = old_entry->path;
-
- if (status == GIT_DELTA_UNMODIFIED &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
- return 0;
-
- if (!new_id)
- new_id = &new_entry->id;
-
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
- uint32_t temp_mode = old_mode;
- const git_index_entry *temp_entry = old_entry;
- const git_oid *temp_id = old_id;
-
- old_entry = new_entry;
- new_entry = temp_entry;
- old_mode = new_mode;
- new_mode = temp_mode;
- old_id = new_id;
- new_id = temp_id;
- }
-
- delta = diff_delta__alloc(diff, status, canonical_path);
- GITERR_CHECK_ALLOC(delta);
- delta->nfiles = 2;
-
- if (!git_index_entry_is_conflict(old_entry)) {
- delta->old_file.size = old_entry->file_size;
- delta->old_file.mode = old_mode;
- git_oid_cpy(&delta->old_file.id, old_id);
- delta->old_file.id_abbrev = GIT_OID_HEXSZ;
- delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID |
- GIT_DIFF_FLAG_EXISTS;
- }
-
- if (!git_index_entry_is_conflict(new_entry)) {
- git_oid_cpy(&delta->new_file.id, new_id);
- delta->new_file.id_abbrev = GIT_OID_HEXSZ;
- delta->new_file.size = new_entry->file_size;
- delta->new_file.mode = new_mode;
- delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
- delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
-
- if (!git_oid_iszero(&new_entry->id))
- delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
- }
-
- return diff_insert_delta(diff, delta, matched_pathspec);
-}
-
-static git_diff_delta *diff_delta__last_for_item(
- git_diff_generated *diff,
- const git_index_entry *item)
-{
- git_diff_delta *delta = git_vector_last(&diff->base.deltas);
- if (!delta)
- return NULL;
-
- switch (delta->status) {
- case GIT_DELTA_UNMODIFIED:
- case GIT_DELTA_DELETED:
- if (git_oid__cmp(&delta->old_file.id, &item->id) == 0)
- return delta;
- break;
- case GIT_DELTA_ADDED:
- if (git_oid__cmp(&delta->new_file.id, &item->id) == 0)
- return delta;
- break;
- case GIT_DELTA_UNREADABLE:
- case GIT_DELTA_UNTRACKED:
- if (diff->base.strcomp(delta->new_file.path, item->path) == 0 &&
- git_oid__cmp(&delta->new_file.id, &item->id) == 0)
- return delta;
- break;
- case GIT_DELTA_MODIFIED:
- if (git_oid__cmp(&delta->old_file.id, &item->id) == 0 ||
- git_oid__cmp(&delta->new_file.id, &item->id) == 0)
- return delta;
- break;
- default:
- break;
- }
-
- return NULL;
-}
-
-static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
-{
- size_t len = strlen(prefix);
-
- /* append '/' at end if needed */
- if (len > 0 && prefix[len - 1] != '/')
- return git_pool_strcat(pool, prefix, "/");
- else
- return git_pool_strndup(pool, prefix, len + 1);
-}
-
-GIT_INLINE(const char *) diff_delta__i2w_path(const git_diff_delta *delta)
-{
- return delta->old_file.path ?
- delta->old_file.path : delta->new_file.path;
-}
-
-int git_diff_delta__i2w_cmp(const void *a, const void *b)
-{
- const git_diff_delta *da = a, *db = b;
- int val = strcmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
- return val ? val : ((int)da->status - (int)db->status);
-}
-
-int git_diff_delta__i2w_casecmp(const void *a, const void *b)
-{
- const git_diff_delta *da = a, *db = b;
- int val = strcasecmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
- return val ? val : ((int)da->status - (int)db->status);
-}
-
-bool git_diff_delta__should_skip(
- const git_diff_options *opts, const git_diff_delta *delta)
-{
- uint32_t flags = opts ? opts->flags : 0;
-
- if (delta->status == GIT_DELTA_UNMODIFIED &&
- (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
- return true;
-
- if (delta->status == GIT_DELTA_IGNORED &&
- (flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
- return true;
-
- if (delta->status == GIT_DELTA_UNTRACKED &&
- (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
- return true;
-
- if (delta->status == GIT_DELTA_UNREADABLE &&
- (flags & GIT_DIFF_INCLUDE_UNREADABLE) == 0)
- return true;
-
- return false;
-}
-
-
-static const char *diff_mnemonic_prefix(
- git_iterator_type_t type, bool left_side)
-{
- const char *pfx = "";
-
- switch (type) {
- case GIT_ITERATOR_TYPE_EMPTY: pfx = "c"; break;
- case GIT_ITERATOR_TYPE_TREE: pfx = "c"; break;
- case GIT_ITERATOR_TYPE_INDEX: pfx = "i"; break;
- case GIT_ITERATOR_TYPE_WORKDIR: pfx = "w"; break;
- case GIT_ITERATOR_TYPE_FS: pfx = left_side ? "1" : "2"; break;
- default: break;
- }
-
- /* note: without a deeper look at pathspecs, there is no easy way
- * to get the (o)bject / (w)ork tree mnemonics working...
- */
-
- return pfx;
-}
-
-void git_diff__set_ignore_case(git_diff *diff, bool ignore_case)
-{
- if (!ignore_case) {
- diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE;
-
- diff->strcomp = git__strcmp;
- diff->strncomp = git__strncmp;
- diff->pfxcomp = git__prefixcmp;
- diff->entrycomp = git_diff__entry_cmp;
-
- git_vector_set_cmp(&diff->deltas, git_diff_delta__cmp);
- } else {
- diff->opts.flags |= GIT_DIFF_IGNORE_CASE;
-
- diff->strcomp = git__strcasecmp;
- diff->strncomp = git__strncasecmp;
- diff->pfxcomp = git__prefixcmp_icase;
- diff->entrycomp = git_diff__entry_icmp;
-
- git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
- }
-
- git_vector_sort(&diff->deltas);
-}
-
-static void diff_generated_free(git_diff *d)
-{
- git_diff_generated *diff = (git_diff_generated *)d;
-
- git_vector_free_deep(&diff->base.deltas);
-
- git_pathspec__vfree(&diff->pathspec);
- git_pool_clear(&diff->base.pool);
-
- git__memzero(diff, sizeof(*diff));
- git__free(diff);
-}
-
-static git_diff_generated *diff_generated_alloc(
- git_repository *repo,
- git_iterator *old_iter,
- git_iterator *new_iter)
-{
- git_diff_generated *diff;
- git_diff_options dflt = GIT_DIFF_OPTIONS_INIT;
-
- assert(repo && old_iter && new_iter);
-
- if ((diff = git__calloc(1, sizeof(git_diff_generated))) == NULL)
- return NULL;
-
- GIT_REFCOUNT_INC(diff);
- diff->base.type = GIT_DIFF_TYPE_GENERATED;
- diff->base.repo = repo;
- diff->base.old_src = old_iter->type;
- diff->base.new_src = new_iter->type;
- diff->base.patch_fn = git_patch_generated_from_diff;
- diff->base.free_fn = diff_generated_free;
- memcpy(&diff->base.opts, &dflt, sizeof(git_diff_options));
-
- git_pool_init(&diff->base.pool, 1);
-
- if (git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) {
- git_diff_free(&diff->base);
- return NULL;
- }
-
- /* Use case-insensitive compare if either iterator has
- * the ignore_case bit set */
- git_diff__set_ignore_case(
- &diff->base,
- git_iterator_ignore_case(old_iter) ||
- git_iterator_ignore_case(new_iter));
-
- return diff;
-}
-
-static int diff_generated_apply_options(
- git_diff_generated *diff,
- const git_diff_options *opts)
-{
- git_config *cfg = NULL;
- git_repository *repo = diff->base.repo;
- git_pool *pool = &diff->base.pool;
- int val;
-
- if (opts) {
- /* copy user options (except case sensitivity info from iterators) */
- bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE);
- memcpy(&diff->base.opts, opts, sizeof(diff->base.opts));
- DIFF_FLAG_SET(diff, GIT_DIFF_IGNORE_CASE, icase);
-
- /* initialize pathspec from options */
- if (git_pathspec__vinit(&diff->pathspec, &opts->pathspec, pool) < 0)
- return -1;
- }
-
- /* flag INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES))
- diff->base.opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
-
- /* flag INCLUDE_UNTRACKED_CONTENT implies INCLUDE_UNTRACKED */
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_SHOW_UNTRACKED_CONTENT))
- diff->base.opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
-
- /* load config values that affect diff behavior */
- if ((val = git_repository_config_snapshot(&cfg, repo)) < 0)
- return val;
-
- if (!git_config__cvar(&val, cfg, GIT_CVAR_SYMLINKS) && val)
- diff->diffcaps |= GIT_DIFFCAPS_HAS_SYMLINKS;
-
- if (!git_config__cvar(&val, cfg, GIT_CVAR_IGNORESTAT) && val)
- diff->diffcaps |= GIT_DIFFCAPS_IGNORE_STAT;
-
- if ((diff->base.opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 &&
- !git_config__cvar(&val, cfg, GIT_CVAR_FILEMODE) && val)
- diff->diffcaps |= GIT_DIFFCAPS_TRUST_MODE_BITS;
-
- if (!git_config__cvar(&val, cfg, GIT_CVAR_TRUSTCTIME) && val)
- diff->diffcaps |= GIT_DIFFCAPS_TRUST_CTIME;
-
- /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
-
- /* If not given explicit `opts`, check `diff.xyz` configs */
- if (!opts) {
- int context = git_config__get_int_force(cfg, "diff.context", 3);
- diff->base.opts.context_lines = context >= 0 ? (uint32_t)context : 3;
-
- /* add other defaults here */
- }
-
- /* Reverse src info if diff is reversed */
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
- git_iterator_type_t tmp_src = diff->base.old_src;
- diff->base.old_src = diff->base.new_src;
- diff->base.new_src = tmp_src;
- }
-
- /* Unset UPDATE_INDEX unless diffing workdir and index */
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) &&
- (!(diff->base.old_src == GIT_ITERATOR_TYPE_WORKDIR ||
- diff->base.new_src == GIT_ITERATOR_TYPE_WORKDIR) ||
- !(diff->base.old_src == GIT_ITERATOR_TYPE_INDEX ||
- diff->base.new_src == GIT_ITERATOR_TYPE_INDEX)))
- diff->base.opts.flags &= ~GIT_DIFF_UPDATE_INDEX;
-
- /* if ignore_submodules not explicitly set, check diff config */
- if (diff->base.opts.ignore_submodules <= 0) {
- git_config_entry *entry;
- git_config__lookup_entry(&entry, cfg, "diff.ignoresubmodules", true);
-
- if (entry && git_submodule_parse_ignore(
- &diff->base.opts.ignore_submodules, entry->value) < 0)
- giterr_clear();
- git_config_entry_free(entry);
- }
-
- /* if either prefix is not set, figure out appropriate value */
- if (!diff->base.opts.old_prefix || !diff->base.opts.new_prefix) {
- const char *use_old = DIFF_OLD_PREFIX_DEFAULT;
- const char *use_new = DIFF_NEW_PREFIX_DEFAULT;
-
- if (git_config__get_bool_force(cfg, "diff.noprefix", 0))
- use_old = use_new = "";
- else if (git_config__get_bool_force(cfg, "diff.mnemonicprefix", 0)) {
- use_old = diff_mnemonic_prefix(diff->base.old_src, true);
- use_new = diff_mnemonic_prefix(diff->base.new_src, false);
- }
-
- if (!diff->base.opts.old_prefix)
- diff->base.opts.old_prefix = use_old;
- if (!diff->base.opts.new_prefix)
- diff->base.opts.new_prefix = use_new;
- }
-
- /* strdup prefix from pool so we're not dependent on external data */
- diff->base.opts.old_prefix = diff_strdup_prefix(pool, diff->base.opts.old_prefix);
- diff->base.opts.new_prefix = diff_strdup_prefix(pool, diff->base.opts.new_prefix);
-
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
- const char *tmp_prefix = diff->base.opts.old_prefix;
- diff->base.opts.old_prefix = diff->base.opts.new_prefix;
- diff->base.opts.new_prefix = tmp_prefix;
- }
-
- git_config_free(cfg);
-
- /* check strdup results for error */
- return (!diff->base.opts.old_prefix || !diff->base.opts.new_prefix) ? -1 : 0;
-}
-
-int git_diff__oid_for_file(
- git_oid *out,
- git_diff *diff,
- const char *path,
- uint16_t mode,
- git_off_t size)
-{
- git_index_entry entry;
-
- memset(&entry, 0, sizeof(entry));
- entry.mode = mode;
- entry.file_size = size;
- entry.path = (char *)path;
-
- return git_diff__oid_for_entry(out, diff, &entry, mode, NULL);
-}
-
-int git_diff__oid_for_entry(
- git_oid *out,
- git_diff *d,
- const git_index_entry *src,
- uint16_t mode,
- const git_oid *update_match)
-{
- git_diff_generated *diff;
- git_buf full_path = GIT_BUF_INIT;
- git_index_entry entry = *src;
- git_filter_list *fl = NULL;
- int error = 0;
-
- assert(d->type == GIT_DIFF_TYPE_GENERATED);
- diff = (git_diff_generated *)d;
-
- memset(out, 0, sizeof(*out));
-
- if (git_buf_joinpath(&full_path,
- git_repository_workdir(diff->base.repo), entry.path) < 0)
- return -1;
-
- if (!mode) {
- struct stat st;
-
- diff->base.perf.stat_calls++;
-
- if (p_stat(full_path.ptr, &st) < 0) {
- error = git_path_set_error(errno, entry.path, "stat");
- git_buf_free(&full_path);
- return error;
- }
-
- git_index_entry__init_from_stat(&entry,
- &st, (diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) != 0);
- }
-
- /* calculate OID for file if possible */
- if (S_ISGITLINK(mode)) {
- git_submodule *sm;
-
- if (!git_submodule_lookup(&sm, diff->base.repo, entry.path)) {
- const git_oid *sm_oid = git_submodule_wd_id(sm);
- if (sm_oid)
- git_oid_cpy(out, sm_oid);
- git_submodule_free(sm);
- } else {
- /* if submodule lookup failed probably just in an intermediate
- * state where some init hasn't happened, so ignore the error
- */
- giterr_clear();
- }
- } else if (S_ISLNK(mode)) {
- error = git_odb__hashlink(out, full_path.ptr);
- diff->base.perf.oid_calculations++;
- } else if (!git__is_sizet(entry.file_size)) {
- giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'",
- entry.path);
- error = -1;
- } else if (!(error = git_filter_list_load(&fl,
- diff->base.repo, NULL, entry.path,
- GIT_FILTER_TO_ODB, GIT_FILTER_ALLOW_UNSAFE)))
- {
- int fd = git_futils_open_ro(full_path.ptr);
- if (fd < 0)
- error = fd;
- else {
- error = git_odb__hashfd_filtered(
- out, fd, (size_t)entry.file_size, GIT_OBJ_BLOB, fl);
- p_close(fd);
- diff->base.perf.oid_calculations++;
- }
-
- git_filter_list_free(fl);
- }
-
- /* update index for entry if requested */
- if (!error && update_match && git_oid_equal(out, update_match)) {
- git_index *idx;
- git_index_entry updated_entry;
-
- memcpy(&updated_entry, &entry, sizeof(git_index_entry));
- updated_entry.mode = mode;
- git_oid_cpy(&updated_entry.id, out);
-
- if (!(error = git_repository_index__weakptr(&idx,
- diff->base.repo))) {
- error = git_index_add(idx, &updated_entry);
- diff->index_updated = true;
- }
- }
-
- git_buf_free(&full_path);
- return error;
-}
-
-typedef struct {
- git_repository *repo;
- git_iterator *old_iter;
- git_iterator *new_iter;
- const git_index_entry *oitem;
- const git_index_entry *nitem;
-} diff_in_progress;
-
-#define MODE_BITS_MASK 0000777
-
-static int maybe_modified_submodule(
- git_delta_t *status,
- git_oid *found_oid,
- git_diff_generated *diff,
- diff_in_progress *info)
-{
- int error = 0;
- git_submodule *sub;
- unsigned int sm_status = 0;
- git_submodule_ignore_t ign = diff->base.opts.ignore_submodules;
-
- *status = GIT_DELTA_UNMODIFIED;
-
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) ||
- ign == GIT_SUBMODULE_IGNORE_ALL)
- return 0;
-
- if ((error = git_submodule_lookup(
- &sub, diff->base.repo, info->nitem->path)) < 0) {
-
- /* GIT_EEXISTS means dir with .git in it was found - ignore it */
- if (error == GIT_EEXISTS) {
- giterr_clear();
- error = 0;
- }
- return error;
- }
-
- if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
- /* ignore it */;
- else if ((error = git_submodule__status(
- &sm_status, NULL, NULL, found_oid, sub, ign)) < 0)
- /* return error below */;
-
- /* check IS_WD_UNMODIFIED because this case is only used
- * when the new side of the diff is the working directory
- */
- else if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
- *status = GIT_DELTA_MODIFIED;
-
- /* now that we have a HEAD OID, check if HEAD moved */
- else if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
- !git_oid_equal(&info->oitem->id, found_oid))
- *status = GIT_DELTA_MODIFIED;
-
- git_submodule_free(sub);
- return error;
-}
-
-static int maybe_modified(
- git_diff_generated *diff,
- diff_in_progress *info)
-{
- git_oid noid;
- git_delta_t status = GIT_DELTA_MODIFIED;
- const git_index_entry *oitem = info->oitem;
- const git_index_entry *nitem = info->nitem;
- unsigned int omode = oitem->mode;
- unsigned int nmode = nitem->mode;
- bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
- bool modified_uncertain = false;
- const char *matched_pathspec;
- int error = 0;
-
- if (!diff_pathspec_match(&matched_pathspec, diff, oitem))
- return 0;
-
- memset(&noid, 0, sizeof(noid));
-
- /* on platforms with no symlinks, preserve mode of existing symlinks */
- if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir &&
- !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
- nmode = omode;
-
- /* on platforms with no execmode, just preserve old mode */
- if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) &&
- (nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) &&
- new_is_workdir)
- nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
-
- /* if one side is a conflict, mark the whole delta as conflicted */
- if (git_index_entry_is_conflict(oitem) ||
- git_index_entry_is_conflict(nitem)) {
- status = GIT_DELTA_CONFLICTED;
-
- /* support "assume unchanged" (poorly, b/c we still stat everything) */
- } else if ((oitem->flags & GIT_IDXENTRY_VALID) != 0) {
- status = GIT_DELTA_UNMODIFIED;
-
- /* support "skip worktree" index bit */
- } else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0) {
- status = GIT_DELTA_UNMODIFIED;
-
- /* if basic type of file changed, then split into delete and add */
- } else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) {
- status = GIT_DELTA_TYPECHANGE;
- }
-
- else if (nmode == GIT_FILEMODE_UNREADABLE) {
- if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
- error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, NULL, nitem);
- return error;
- }
-
- else {
- if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
- error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem);
- return error;
- }
-
- /* if oids and modes match (and are valid), then file is unmodified */
- } else if (git_oid_equal(&oitem->id, &nitem->id) &&
- omode == nmode &&
- !git_oid_iszero(&oitem->id)) {
- status = GIT_DELTA_UNMODIFIED;
-
- /* if we have an unknown OID and a workdir iterator, then check some
- * circumstances that can accelerate things or need special handling
- */
- } else if (git_oid_iszero(&nitem->id) && new_is_workdir) {
- bool use_ctime =
- ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0);
- git_index *index = git_iterator_index(info->new_iter);
-
- status = GIT_DELTA_UNMODIFIED;
-
- if (S_ISGITLINK(nmode)) {
- if ((error = maybe_modified_submodule(&status, &noid, diff, info)) < 0)
- return error;
- }
-
- /* if the stat data looks different, then mark modified - this just
- * means that the OID will be recalculated below to confirm change
- */
- else if (omode != nmode || oitem->file_size != nitem->file_size) {
- status = GIT_DELTA_MODIFIED;
- modified_uncertain =
- (oitem->file_size <= 0 && nitem->file_size > 0);
- }
- else if (!git_index_time_eq(&oitem->mtime, &nitem->mtime) ||
- (use_ctime && !git_index_time_eq(&oitem->ctime, &nitem->ctime)) ||
- oitem->ino != nitem->ino ||
- oitem->uid != nitem->uid ||
- oitem->gid != nitem->gid ||
- git_index_entry_newer_than_index(nitem, index))
- {
- status = GIT_DELTA_MODIFIED;
- modified_uncertain = true;
- }
-
- /* if mode is GITLINK and submodules are ignored, then skip */
- } else if (S_ISGITLINK(nmode) &&
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES)) {
- status = GIT_DELTA_UNMODIFIED;
- }
-
- /* if we got here and decided that the files are modified, but we
- * haven't calculated the OID of the new item, then calculate it now
- */
- if (modified_uncertain && git_oid_iszero(&nitem->id)) {
- const git_oid *update_check =
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) && omode == nmode ?
- &oitem->id : NULL;
-
- if ((error = git_diff__oid_for_entry(
- &noid, &diff->base, nitem, nmode, update_check)) < 0)
- return error;
-
- /* if oid matches, then mark unmodified (except submodules, where
- * the filesystem content may be modified even if the oid still
- * matches between the index and the workdir HEAD)
- */
- if (omode == nmode && !S_ISGITLINK(omode) &&
- git_oid_equal(&oitem->id, &noid))
- status = GIT_DELTA_UNMODIFIED;
- }
-
- /* If we want case changes, then break this into a delete of the old
- * and an add of the new so that consumers can act accordingly (eg,
- * checkout will update the case on disk.)
- */
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE) &&
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_CASECHANGE) &&
- strcmp(oitem->path, nitem->path) != 0) {
-
- if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
- error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem);
-
- return error;
- }
-
- return diff_delta__from_two(
- diff, status, oitem, omode, nitem, nmode,
- git_oid_iszero(&noid) ? NULL : &noid, matched_pathspec);
-}
-
-static bool entry_is_prefixed(
- git_diff_generated *diff,
- const git_index_entry *item,
- const git_index_entry *prefix_item)
-{
- size_t pathlen;
-
- if (!item || diff->base.pfxcomp(item->path, prefix_item->path) != 0)
- return false;
-
- pathlen = strlen(prefix_item->path);
-
- return (prefix_item->path[pathlen - 1] == '/' ||
- item->path[pathlen] == '\0' ||
- item->path[pathlen] == '/');
-}
-
-static int iterator_current(
- const git_index_entry **entry,
- git_iterator *iterator)
-{
- int error;
-
- if ((error = git_iterator_current(entry, iterator)) == GIT_ITEROVER) {
- *entry = NULL;
- error = 0;
- }
-
- return error;
-}
-
-static int iterator_advance(
- const git_index_entry **entry,
- git_iterator *iterator)
-{
- const git_index_entry *prev_entry = *entry;
- int cmp, error;
-
- /* if we're looking for conflicts, we only want to report
- * one conflict for each file, instead of all three sides.
- * so if this entry is a conflict for this file, and the
- * previous one was a conflict for the same file, skip it.
- */
- while ((error = git_iterator_advance(entry, iterator)) == 0) {
- if (!(iterator->flags & GIT_ITERATOR_INCLUDE_CONFLICTS) ||
- !git_index_entry_is_conflict(prev_entry) ||
- !git_index_entry_is_conflict(*entry))
- break;
-
- cmp = (iterator->flags & GIT_ITERATOR_IGNORE_CASE) ?
- strcasecmp(prev_entry->path, (*entry)->path) :
- strcmp(prev_entry->path, (*entry)->path);
-
- if (cmp)
- break;
- }
-
- if (error == GIT_ITEROVER) {
- *entry = NULL;
- error = 0;
- }
-
- return error;
-}
-
-static int iterator_advance_into(
- const git_index_entry **entry,
- git_iterator *iterator)
-{
- int error;
-
- if ((error = git_iterator_advance_into(entry, iterator)) == GIT_ITEROVER) {
- *entry = NULL;
- error = 0;
- }
-
- return error;
-}
-
-static int iterator_advance_over(
- const git_index_entry **entry,
- git_iterator_status_t *status,
- git_iterator *iterator)
-{
- int error = git_iterator_advance_over(entry, status, iterator);
-
- if (error == GIT_ITEROVER) {
- *entry = NULL;
- error = 0;
- }
-
- return error;
-}
-
-static int handle_unmatched_new_item(
- git_diff_generated *diff, diff_in_progress *info)
-{
- int error = 0;
- const git_index_entry *nitem = info->nitem;
- git_delta_t delta_type = GIT_DELTA_UNTRACKED;
- bool contains_oitem;
-
- /* check if this is a prefix of the other side */
- contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
-
- /* update delta_type if this item is conflicted */
- if (git_index_entry_is_conflict(nitem))
- delta_type = GIT_DELTA_CONFLICTED;
-
- /* update delta_type if this item is ignored */
- else if (git_iterator_current_is_ignored(info->new_iter))
- delta_type = GIT_DELTA_IGNORED;
-
- if (nitem->mode == GIT_FILEMODE_TREE) {
- bool recurse_into_dir = contains_oitem;
-
- /* check if user requests recursion into this type of dir */
- recurse_into_dir = contains_oitem ||
- (delta_type == GIT_DELTA_UNTRACKED &&
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) ||
- (delta_type == GIT_DELTA_IGNORED &&
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
-
- /* do not advance into directories that contain a .git file */
- if (recurse_into_dir && !contains_oitem) {
- git_buf *full = NULL;
- if (git_iterator_current_workdir_path(&full, info->new_iter) < 0)
- return -1;
- if (full && git_path_contains(full, DOT_GIT)) {
- /* TODO: warning if not a valid git repository */
- recurse_into_dir = false;
- }
- }
-
- /* still have to look into untracked directories to match core git -
- * with no untracked files, directory is treated as ignored
- */
- if (!recurse_into_dir &&
- delta_type == GIT_DELTA_UNTRACKED &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS))
- {
- git_diff_delta *last;
- git_iterator_status_t untracked_state;
-
- /* attempt to insert record for this directory */
- if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0)
- return error;
-
- /* if delta wasn't created (because of rules), just skip ahead */
- last = diff_delta__last_for_item(diff, nitem);
- if (!last)
- return iterator_advance(&info->nitem, info->new_iter);
-
- /* iterate into dir looking for an actual untracked file */
- if ((error = iterator_advance_over(
- &info->nitem, &untracked_state, info->new_iter)) < 0)
- return error;
-
- /* if we found nothing that matched our pathlist filter, exclude */
- if (untracked_state == GIT_ITERATOR_STATUS_FILTERED) {
- git_vector_pop(&diff->base.deltas);
- git__free(last);
- }
-
- /* if we found nothing or just ignored items, update the record */
- if (untracked_state == GIT_ITERATOR_STATUS_IGNORED ||
- untracked_state == GIT_ITERATOR_STATUS_EMPTY) {
- last->status = GIT_DELTA_IGNORED;
-
- /* remove the record if we don't want ignored records */
- if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED)) {
- git_vector_pop(&diff->base.deltas);
- git__free(last);
- }
- }
-
- return 0;
- }
-
- /* try to advance into directory if necessary */
- if (recurse_into_dir) {
- error = iterator_advance_into(&info->nitem, info->new_iter);
-
- /* if directory is empty, can't advance into it, so skip it */
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = iterator_advance(&info->nitem, info->new_iter);
- }
-
- return error;
- }
- }
-
- else if (delta_type == GIT_DELTA_IGNORED &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) &&
- git_iterator_current_tree_is_ignored(info->new_iter))
- /* item contained in ignored directory, so skip over it */
- return iterator_advance(&info->nitem, info->new_iter);
-
- else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR) {
- if (delta_type != GIT_DELTA_CONFLICTED)
- delta_type = GIT_DELTA_ADDED;
- }
-
- else if (nitem->mode == GIT_FILEMODE_COMMIT) {
- /* ignore things that are not actual submodules */
- if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) {
- giterr_clear();
- delta_type = GIT_DELTA_IGNORED;
-
- /* if this contains a tracked item, treat as normal TREE */
- if (contains_oitem) {
- error = iterator_advance_into(&info->nitem, info->new_iter);
- if (error != GIT_ENOTFOUND)
- return error;
-
- giterr_clear();
- return iterator_advance(&info->nitem, info->new_iter);
- }
- }
- }
-
- else if (nitem->mode == GIT_FILEMODE_UNREADABLE) {
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED))
- delta_type = GIT_DELTA_UNTRACKED;
- else
- delta_type = GIT_DELTA_UNREADABLE;
- }
-
- /* Actually create the record for this item if necessary */
- if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0)
- return error;
-
- /* If user requested TYPECHANGE records, then check for that instead of
- * just generating an ADDED/UNTRACKED record
- */
- if (delta_type != GIT_DELTA_IGNORED &&
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
- contains_oitem)
- {
- /* this entry was prefixed with a tree - make TYPECHANGE */
- git_diff_delta *last = diff_delta__last_for_item(diff, nitem);
- if (last) {
- last->status = GIT_DELTA_TYPECHANGE;
- last->old_file.mode = GIT_FILEMODE_TREE;
- }
- }
-
- return iterator_advance(&info->nitem, info->new_iter);
-}
-
-static int handle_unmatched_old_item(
- git_diff_generated *diff, diff_in_progress *info)
-{
- git_delta_t delta_type = GIT_DELTA_DELETED;
- int error;
-
- /* update delta_type if this item is conflicted */
- if (git_index_entry_is_conflict(info->oitem))
- delta_type = GIT_DELTA_CONFLICTED;
-
- if ((error = diff_delta__from_one(diff, delta_type, info->oitem, NULL)) < 0)
- return error;
-
- /* if we are generating TYPECHANGE records then check for that
- * instead of just generating a DELETE record
- */
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
- entry_is_prefixed(diff, info->nitem, info->oitem))
- {
- /* this entry has become a tree! convert to TYPECHANGE */
- git_diff_delta *last = diff_delta__last_for_item(diff, info->oitem);
- if (last) {
- last->status = GIT_DELTA_TYPECHANGE;
- last->new_file.mode = GIT_FILEMODE_TREE;
- }
-
- /* If new_iter is a workdir iterator, then this situation
- * will certainly be followed by a series of untracked items.
- * Unless RECURSE_UNTRACKED_DIRS is set, skip over them...
- */
- if (S_ISDIR(info->nitem->mode) &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS))
- return iterator_advance(&info->nitem, info->new_iter);
- }
-
- return iterator_advance(&info->oitem, info->old_iter);
-}
-
-static int handle_matched_item(
- git_diff_generated *diff, diff_in_progress *info)
-{
- int error = 0;
-
- if ((error = maybe_modified(diff, info)) < 0)
- return error;
-
- if (!(error = iterator_advance(&info->oitem, info->old_iter)))
- error = iterator_advance(&info->nitem, info->new_iter);
-
- return error;
-}
-
-int git_diff__from_iterators(
- git_diff **out,
- git_repository *repo,
- git_iterator *old_iter,
- git_iterator *new_iter,
- const git_diff_options *opts)
-{
- git_diff_generated *diff;
- diff_in_progress info;
- int error = 0;
-
- *out = NULL;
-
- diff = diff_generated_alloc(repo, old_iter, new_iter);
- GITERR_CHECK_ALLOC(diff);
-
- info.repo = repo;
- info.old_iter = old_iter;
- info.new_iter = new_iter;
-
- /* make iterators have matching icase behavior */
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
- git_iterator_set_ignore_case(old_iter, true);
- git_iterator_set_ignore_case(new_iter, true);
- }
-
- /* finish initialization */
- if ((error = diff_generated_apply_options(diff, opts)) < 0)
- goto cleanup;
-
- if ((error = iterator_current(&info.oitem, old_iter)) < 0 ||
- (error = iterator_current(&info.nitem, new_iter)) < 0)
- goto cleanup;
-
- /* run iterators building diffs */
- while (!error && (info.oitem || info.nitem)) {
- int cmp;
-
- /* report progress */
- if (opts && opts->progress_cb) {
- if ((error = opts->progress_cb(&diff->base,
- info.oitem ? info.oitem->path : NULL,
- info.nitem ? info.nitem->path : NULL,
- opts->payload)))
- break;
- }
-
- cmp = info.oitem ?
- (info.nitem ? diff->base.entrycomp(info.oitem, info.nitem) : -1) : 1;
-
- /* create DELETED records for old items not matched in new */
- if (cmp < 0)
- error = handle_unmatched_old_item(diff, &info);
-
- /* create ADDED, TRACKED, or IGNORED records for new items not
- * matched in old (and/or descend into directories as needed)
- */
- else if (cmp > 0)
- error = handle_unmatched_new_item(diff, &info);
-
- /* otherwise item paths match, so create MODIFIED record
- * (or ADDED and DELETED pair if type changed)
- */
- else
- error = handle_matched_item(diff, &info);
- }
-
- diff->base.perf.stat_calls +=
- old_iter->stat_calls + new_iter->stat_calls;
-
-cleanup:
- if (!error)
- *out = &diff->base;
- else
- git_diff_free(&diff->base);
-
- return error;
-}
-
-#define DIFF_FROM_ITERATORS(MAKE_FIRST, FLAGS_FIRST, MAKE_SECOND, FLAGS_SECOND) do { \
- git_iterator *a = NULL, *b = NULL; \
- char *pfx = (opts && !(opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) ? \
- git_pathspec_prefix(&opts->pathspec) : NULL; \
- git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT, \
- b_opts = GIT_ITERATOR_OPTIONS_INIT; \
- a_opts.flags = FLAGS_FIRST; \
- a_opts.start = pfx; \
- a_opts.end = pfx; \
- b_opts.flags = FLAGS_SECOND; \
- b_opts.start = pfx; \
- b_opts.end = pfx; \
- GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \
- if (opts && (opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) { \
- a_opts.pathlist.strings = opts->pathspec.strings; \
- a_opts.pathlist.count = opts->pathspec.count; \
- b_opts.pathlist.strings = opts->pathspec.strings; \
- b_opts.pathlist.count = opts->pathspec.count; \
- } \
- if (!error && !(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \
- error = git_diff__from_iterators(&diff, repo, a, b, opts); \
- git__free(pfx); git_iterator_free(a); git_iterator_free(b); \
-} while (0)
-
-int git_diff_tree_to_tree(
- git_diff **out,
- git_repository *repo,
- git_tree *old_tree,
- git_tree *new_tree,
- const git_diff_options *opts)
-{
- git_diff *diff = NULL;
- git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE;
- int error = 0;
-
- assert(out && repo);
-
- *out = NULL;
-
- /* for tree to tree diff, be case sensitive even if the index is
- * currently case insensitive, unless the user explicitly asked
- * for case insensitivity
- */
- if (opts && (opts->flags & GIT_DIFF_IGNORE_CASE) != 0)
- iflag = GIT_ITERATOR_IGNORE_CASE;
-
- DIFF_FROM_ITERATORS(
- git_iterator_for_tree(&a, old_tree, &a_opts), iflag,
- git_iterator_for_tree(&b, new_tree, &b_opts), iflag
- );
-
- if (!error)
- *out = diff;
-
- return error;
-}
-
-static int diff_load_index(git_index **index, git_repository *repo)
-{
- int error = git_repository_index__weakptr(index, repo);
-
- /* reload the repository index when user did not pass one in */
- if (!error && git_index_read(*index, false) < 0)
- giterr_clear();
-
- return error;
-}
-
-int git_diff_tree_to_index(
- git_diff **out,
- git_repository *repo,
- git_tree *old_tree,
- git_index *index,
- const git_diff_options *opts)
-{
- git_diff *diff = NULL;
- git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE |
- GIT_ITERATOR_INCLUDE_CONFLICTS;
- bool index_ignore_case = false;
- int error = 0;
-
- assert(out && repo);
-
- *out = NULL;
-
- if (!index && (error = diff_load_index(&index, repo)) < 0)
- return error;
-
- index_ignore_case = index->ignore_case;
-
- DIFF_FROM_ITERATORS(
- git_iterator_for_tree(&a, old_tree, &a_opts), iflag,
- git_iterator_for_index(&b, repo, index, &b_opts), iflag
- );
-
- /* if index is in case-insensitive order, re-sort deltas to match */
- if (!error && index_ignore_case)
- git_diff__set_ignore_case(diff, true);
-
- if (!error)
- *out = diff;
-
- return error;
-}
-
-int git_diff_index_to_workdir(
- git_diff **out,
- git_repository *repo,
- git_index *index,
- const git_diff_options *opts)
-{
- git_diff *diff = NULL;
- int error = 0;
-
- assert(out && repo);
-
- *out = NULL;
-
- if (!index && (error = diff_load_index(&index, repo)) < 0)
- return error;
-
- DIFF_FROM_ITERATORS(
- git_iterator_for_index(&a, repo, index, &a_opts),
- GIT_ITERATOR_INCLUDE_CONFLICTS,
-
- git_iterator_for_workdir(&b, repo, index, NULL, &b_opts),
- GIT_ITERATOR_DONT_AUTOEXPAND
- );
-
- if (!error && (diff->opts.flags & GIT_DIFF_UPDATE_INDEX) != 0 &&
- ((git_diff_generated *)diff)->index_updated)
- error = git_index_write(index);
-
- if (!error)
- *out = diff;
-
- return error;
-}
-
-int git_diff_tree_to_workdir(
- git_diff **out,
- git_repository *repo,
- git_tree *old_tree,
- const git_diff_options *opts)
-{
- git_diff *diff = NULL;
- git_index *index;
- int error = 0;
-
- assert(out && repo);
-
- *out = NULL;
-
- if ((error = git_repository_index__weakptr(&index, repo)))
- return error;
-
- DIFF_FROM_ITERATORS(
- git_iterator_for_tree(&a, old_tree, &a_opts), 0,
- git_iterator_for_workdir(&b, repo, index, old_tree, &b_opts), GIT_ITERATOR_DONT_AUTOEXPAND
- );
-
- if (!error)
- *out = diff;
-
- return error;
-}
-
-int git_diff_tree_to_workdir_with_index(
- git_diff **out,
- git_repository *repo,
- git_tree *tree,
- const git_diff_options *opts)
-{
- git_diff *d1 = NULL, *d2 = NULL;
- git_index *index = NULL;
- int error = 0;
-
- assert(out && repo);
-
- *out = NULL;
-
- if ((error = diff_load_index(&index, repo)) < 0)
- return error;
-
- if (!(error = git_diff_tree_to_index(&d1, repo, tree, index, opts)) &&
- !(error = git_diff_index_to_workdir(&d2, repo, index, opts)))
- error = git_diff_merge(d1, d2);
-
- git_diff_free(d2);
-
- if (error) {
- git_diff_free(d1);
- d1 = NULL;
- }
-
- *out = d1;
- return error;
-}
-
-int git_diff_index_to_index(
- git_diff **out,
- git_repository *repo,
- git_index *old_index,
- git_index *new_index,
- const git_diff_options *opts)
-{
- git_diff *diff;
- int error = 0;
-
- assert(out && old_index && new_index);
-
- *out = NULL;
-
- DIFF_FROM_ITERATORS(
- git_iterator_for_index(&a, repo, old_index, &a_opts), GIT_ITERATOR_DONT_IGNORE_CASE,
- git_iterator_for_index(&b, repo, new_index, &b_opts), GIT_ITERATOR_DONT_IGNORE_CASE
- );
-
- /* if index is in case-insensitive order, re-sort deltas to match */
- if (!error && (old_index->ignore_case || new_index->ignore_case))
- git_diff__set_ignore_case(diff, true);
-
- if (!error)
- *out = diff;
-
- return error;
-}
-
-int git_diff__paired_foreach(
- git_diff *head2idx,
- git_diff *idx2wd,
- int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
- void *payload)
-{
- int cmp, error = 0;
- git_diff_delta *h2i, *i2w;
- size_t i, j, i_max, j_max;
- int (*strcomp)(const char *, const char *) = git__strcmp;
- bool h2i_icase, i2w_icase, icase_mismatch;
-
- i_max = head2idx ? head2idx->deltas.length : 0;
- j_max = idx2wd ? idx2wd->deltas.length : 0;
- if (!i_max && !j_max)
- return 0;
-
- /* At some point, tree-to-index diffs will probably never ignore case,
- * even if that isn't true now. Index-to-workdir diffs may or may not
- * ignore case, but the index filename for the idx2wd diff should
- * still be using the canonical case-preserving name.
- *
- * Therefore the main thing we need to do here is make sure the diffs
- * are traversed in a compatible order. To do this, we temporarily
- * resort a mismatched diff to get the order correct.
- *
- * In order to traverse renames in the index->workdir, we need to
- * ensure that we compare the index name on both sides, so we
- * always sort by the old name in the i2w list.
- */
- h2i_icase = head2idx != NULL && git_diff_is_sorted_icase(head2idx);
- i2w_icase = idx2wd != NULL && git_diff_is_sorted_icase(idx2wd);
-
- icase_mismatch =
- (head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
-
- if (icase_mismatch && h2i_icase) {
- git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
- git_vector_sort(&head2idx->deltas);
- }
-
- if (i2w_icase && !icase_mismatch) {
- strcomp = git__strcasecmp;
-
- git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_casecmp);
- git_vector_sort(&idx2wd->deltas);
- } else if (idx2wd != NULL) {
- git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_cmp);
- git_vector_sort(&idx2wd->deltas);
- }
-
- for (i = 0, j = 0; i < i_max || j < j_max; ) {
- h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
- i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
-
- cmp = !i2w ? -1 : !h2i ? 1 :
- strcomp(h2i->new_file.path, i2w->old_file.path);
-
- if (cmp < 0) {
- i++; i2w = NULL;
- } else if (cmp > 0) {
- j++; h2i = NULL;
- } else {
- i++; j++;
- }
-
- if ((error = cb(h2i, i2w, payload)) != 0) {
- giterr_set_after_callback(error);
- break;
- }
- }
-
- /* restore case-insensitive delta sort */
- if (icase_mismatch && h2i_icase) {
- git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
- git_vector_sort(&head2idx->deltas);
- }
-
- /* restore idx2wd sort by new path */
- if (idx2wd != NULL) {
- git_vector_set_cmp(&idx2wd->deltas,
- i2w_icase ? git_diff_delta__casecmp : git_diff_delta__cmp);
- git_vector_sort(&idx2wd->deltas);
- }
-
- return error;
-}
-
-int git_diff__commit(
- git_diff **out,
- git_repository *repo,
- const git_commit *commit,
- const git_diff_options *opts)
-{
- git_commit *parent = NULL;
- git_diff *commit_diff = NULL;
- git_tree *old_tree = NULL, *new_tree = NULL;
- size_t parents;
- int error = 0;
-
- *out = NULL;
-
- if ((parents = git_commit_parentcount(commit)) > 1) {
- char commit_oidstr[GIT_OID_HEXSZ + 1];
-
- error = -1;
- giterr_set(GITERR_INVALID, "Commit %s is a merge commit",
- git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit)));
- goto on_error;
- }
-
- if (parents > 0)
- if ((error = git_commit_parent(&parent, commit, 0)) < 0 ||
- (error = git_commit_tree(&old_tree, parent)) < 0)
- goto on_error;
-
- if ((error = git_commit_tree(&new_tree, commit)) < 0 ||
- (error = git_diff_tree_to_tree(&commit_diff, repo, old_tree, new_tree, opts)) < 0)
- goto on_error;
-
- *out = commit_diff;
-
-on_error:
- git_tree_free(new_tree);
- git_tree_free(old_tree);
- git_commit_free(parent);
-
- return error;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_diff_generate_h__
-#define INCLUDE_diff_generate_h__
-
-enum {
- GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
- GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */
- GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
- GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
- GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
-};
-
-#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
-#define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
-
-enum {
- GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */
- GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */
- GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */
- GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */
- GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */
- GIT_DIFF_FLAG__LOADED = (1 << 12), /* file data has been loaded */
-
- GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */
- GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */
- GIT_DIFF_FLAG__IS_RENAME_TARGET = (1 << 18),
- GIT_DIFF_FLAG__IS_RENAME_SOURCE = (1 << 19),
- GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20),
-};
-
-#define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF)
-
-#define GIT_DIFF__VERBOSE (1 << 30)
-
-extern void git_diff_addref(git_diff *diff);
-
-extern bool git_diff_delta__should_skip(
- const git_diff_options *opts, const git_diff_delta *delta);
-
-extern int git_diff__from_iterators(
- git_diff **diff_ptr,
- git_repository *repo,
- git_iterator *old_iter,
- git_iterator *new_iter,
- const git_diff_options *opts);
-
-extern int git_diff__commit(
- git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts);
-
-extern int git_diff__paired_foreach(
- git_diff *idx2head,
- git_diff *wd2idx,
- int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
- void *payload);
-
-/* Merge two `git_diff`s according to the callback given by `cb`. */
-
-typedef git_diff_delta *(*git_diff__merge_cb)(
- const git_diff_delta *left,
- const git_diff_delta *right,
- git_pool *pool);
-
-extern int git_diff__merge(
- git_diff *onto, const git_diff *from, git_diff__merge_cb cb);
-
-extern git_diff_delta *git_diff__merge_like_cgit(
- const git_diff_delta *a,
- const git_diff_delta *b,
- git_pool *pool);
-
-/* Duplicate a `git_diff_delta` out of the `git_pool` */
-extern git_diff_delta *git_diff__delta_dup(
- const git_diff_delta *d, git_pool *pool);
-
-extern int git_diff__oid_for_file(
- git_oid *out,
- git_diff *diff,
- const char *path,
- uint16_t mode,
- git_off_t size);
-
-extern int git_diff__oid_for_entry(
- git_oid *out,
- git_diff *diff,
- const git_index_entry *src,
- uint16_t mode,
- const git_oid *update_match);
-
-/*
- * Sometimes a git_diff_file will have a zero size; this attempts to
- * fill in the size without loading the blob if possible. If that is
- * not possible, then it will return the git_odb_object that had to be
- * loaded and the caller can use it or dispose of it as needed.
- */
-GIT_INLINE(int) git_diff_file__resolve_zero_size(
- git_diff_file *file, git_odb_object **odb_obj, git_repository *repo)
-{
- int error;
- git_odb *odb;
- size_t len;
- git_otype type;
-
- if ((error = git_repository_odb(&odb, repo)) < 0)
- return error;
-
- error = git_odb__read_header_or_object(
- odb_obj, &len, &type, odb, &file->id);
-
- git_odb_free(odb);
-
- if (!error)
- file->size = (git_off_t)len;
-
- return error;
-}
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "diff.h"
-#include "diff_parse.h"
-#include "patch.h"
-#include "patch_parse.h"
-
-static void diff_parsed_free(git_diff *d)
-{
- git_diff_parsed *diff = (git_diff_parsed *)d;
- git_patch *patch;
- size_t i;
-
- git_vector_foreach(&diff->patches, i, patch)
- git_patch_free(patch);
-
- git_vector_free(&diff->patches);
-
- git_vector_free(&diff->base.deltas);
- git_pool_clear(&diff->base.pool);
-
- git__memzero(diff, sizeof(*diff));
- git__free(diff);
-}
-
-static git_diff_parsed *diff_parsed_alloc(void)
-{
- git_diff_parsed *diff;
-
- if ((diff = git__calloc(1, sizeof(git_diff_parsed))) == NULL)
- return NULL;
-
- GIT_REFCOUNT_INC(diff);
- diff->base.type = GIT_DIFF_TYPE_PARSED;
- diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE;
- diff->base.strcomp = git__strcmp;
- diff->base.strncomp = git__strncmp;
- diff->base.pfxcomp = git__prefixcmp;
- diff->base.entrycomp = git_diff__entry_cmp;
- diff->base.patch_fn = git_patch_parsed_from_diff;
- diff->base.free_fn = diff_parsed_free;
-
- git_pool_init(&diff->base.pool, 1);
-
- if (git_vector_init(&diff->patches, 0, NULL) < 0 ||
- git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) {
- git_diff_free(&diff->base);
- return NULL;
- }
-
- git_vector_set_cmp(&diff->base.deltas, git_diff_delta__cmp);
-
- return diff;
-}
-
-int git_diff_from_buffer(
- git_diff **out,
- const char *content,
- size_t content_len)
-{
- git_diff_parsed *diff;
- git_patch *patch;
- git_patch_parse_ctx *ctx = NULL;
- int error = 0;
-
- *out = NULL;
-
- diff = diff_parsed_alloc();
- GITERR_CHECK_ALLOC(diff);
-
- ctx = git_patch_parse_ctx_init(content, content_len, NULL);
- GITERR_CHECK_ALLOC(ctx);
-
- while (ctx->remain_len) {
- if ((error = git_patch_parse(&patch, ctx)) < 0)
- break;
-
- git_vector_insert(&diff->patches, patch);
- git_vector_insert(&diff->base.deltas, patch->delta);
- }
-
- if (error == GIT_ENOTFOUND && git_vector_length(&diff->patches) > 0) {
- giterr_clear();
- error = 0;
- }
-
- git_patch_parse_ctx_free(ctx);
-
- if (error < 0)
- git_diff_free(&diff->base);
- else
- *out = &diff->base;
-
- return error;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_diff_parse_h__
-#define INCLUDE_diff_parse_h__
-
-#include "diff.h"
-
-typedef struct {
- struct git_diff base;
-
- git_vector patches;
-} git_diff_parsed;
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "diff.h"
-#include "diff_file.h"
-#include "patch_generate.h"
-#include "fileops.h"
-#include "zstream.h"
-#include "blob.h"
-#include "delta.h"
-#include "git2/sys/diff.h"
-
-typedef struct {
- git_diff_format_t format;
- git_diff_line_cb print_cb;
- void *payload;
-
- git_buf *buf;
- git_diff_line line;
-
- const char *old_prefix;
- const char *new_prefix;
- uint32_t flags;
- int id_strlen;
-
- int (*strcomp)(const char *, const char *);
-} diff_print_info;
-
-static int diff_print_info_init__common(
- diff_print_info *pi,
- git_buf *out,
- git_repository *repo,
- git_diff_format_t format,
- git_diff_line_cb cb,
- void *payload)
-{
- pi->format = format;
- pi->print_cb = cb;
- pi->payload = payload;
- pi->buf = out;
-
- if (!pi->id_strlen) {
- if (!repo)
- pi->id_strlen = GIT_ABBREV_DEFAULT;
- else if (git_repository__cvar(&pi->id_strlen, repo, GIT_CVAR_ABBREV) < 0)
- return -1;
- }
-
- if (pi->id_strlen > GIT_OID_HEXSZ)
- pi->id_strlen = GIT_OID_HEXSZ;
-
- memset(&pi->line, 0, sizeof(pi->line));
- pi->line.old_lineno = -1;
- pi->line.new_lineno = -1;
- pi->line.num_lines = 1;
-
- return 0;
-}
-
-static int diff_print_info_init_fromdiff(
- diff_print_info *pi,
- git_buf *out,
- git_diff *diff,
- git_diff_format_t format,
- git_diff_line_cb cb,
- void *payload)
-{
- git_repository *repo = diff ? diff->repo : NULL;
-
- memset(pi, 0, sizeof(diff_print_info));
-
- if (diff) {
- pi->flags = diff->opts.flags;
- pi->id_strlen = diff->opts.id_abbrev;
- pi->old_prefix = diff->opts.old_prefix;
- pi->new_prefix = diff->opts.new_prefix;
-
- pi->strcomp = diff->strcomp;
- }
-
- return diff_print_info_init__common(pi, out, repo, format, cb, payload);
-}
-
-static int diff_print_info_init_frompatch(
- diff_print_info *pi,
- git_buf *out,
- git_patch *patch,
- git_diff_format_t format,
- git_diff_line_cb cb,
- void *payload)
-{
- assert(patch);
-
- memset(pi, 0, sizeof(diff_print_info));
-
- pi->flags = patch->diff_opts.flags;
- pi->id_strlen = patch->diff_opts.id_abbrev;
- pi->old_prefix = patch->diff_opts.old_prefix;
- pi->new_prefix = patch->diff_opts.new_prefix;
-
- return diff_print_info_init__common(pi, out, patch->repo, format, cb, payload);
-}
-
-static char diff_pick_suffix(int mode)
-{
- if (S_ISDIR(mode))
- return '/';
- else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */
- /* in git, modes are very regular, so we must have 0100755 mode */
- return '*';
- else
- return ' ';
-}
-
-char git_diff_status_char(git_delta_t status)
-{
- char code;
-
- switch (status) {
- case GIT_DELTA_ADDED: code = 'A'; break;
- case GIT_DELTA_DELETED: code = 'D'; break;
- case GIT_DELTA_MODIFIED: code = 'M'; break;
- case GIT_DELTA_RENAMED: code = 'R'; break;
- case GIT_DELTA_COPIED: code = 'C'; break;
- case GIT_DELTA_IGNORED: code = 'I'; break;
- case GIT_DELTA_UNTRACKED: code = '?'; break;
- case GIT_DELTA_UNREADABLE: code = 'X'; break;
- default: code = ' '; break;
- }
-
- return code;
-}
-
-static int diff_print_one_name_only(
- const git_diff_delta *delta, float progress, void *data)
-{
- diff_print_info *pi = data;
- git_buf *out = pi->buf;
-
- GIT_UNUSED(progress);
-
- if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 &&
- delta->status == GIT_DELTA_UNMODIFIED)
- return 0;
-
- git_buf_clear(out);
- git_buf_puts(out, delta->new_file.path);
- git_buf_putc(out, '\n');
- if (git_buf_oom(out))
- return -1;
-
- pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
- pi->line.content = git_buf_cstr(out);
- pi->line.content_len = git_buf_len(out);
-
- return pi->print_cb(delta, NULL, &pi->line, pi->payload);
-}
-
-static int diff_print_one_name_status(
- const git_diff_delta *delta, float progress, void *data)
-{
- diff_print_info *pi = data;
- git_buf *out = pi->buf;
- char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
- int(*strcomp)(const char *, const char *) = pi->strcomp ?
- pi->strcomp : git__strcmp;
-
- GIT_UNUSED(progress);
-
- if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
- return 0;
-
- old_suffix = diff_pick_suffix(delta->old_file.mode);
- new_suffix = diff_pick_suffix(delta->new_file.mode);
-
- git_buf_clear(out);
-
- if (delta->old_file.path != delta->new_file.path &&
- strcomp(delta->old_file.path,delta->new_file.path) != 0)
- git_buf_printf(out, "%c\t%s%c %s%c\n", code,
- delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
- else if (delta->old_file.mode != delta->new_file.mode &&
- delta->old_file.mode != 0 && delta->new_file.mode != 0)
- git_buf_printf(out, "%c\t%s%c %s%c\n", code,
- delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
- else if (old_suffix != ' ')
- git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
- else
- git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path);
- if (git_buf_oom(out))
- return -1;
-
- pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
- pi->line.content = git_buf_cstr(out);
- pi->line.content_len = git_buf_len(out);
-
- return pi->print_cb(delta, NULL, &pi->line, pi->payload);
-}
-
-static int diff_print_one_raw(
- const git_diff_delta *delta, float progress, void *data)
-{
- diff_print_info *pi = data;
- git_buf *out = pi->buf;
- int id_abbrev;
- char code = git_diff_status_char(delta->status);
- char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
-
- GIT_UNUSED(progress);
-
- if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
- return 0;
-
- git_buf_clear(out);
-
- id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev :
- delta->new_file.id_abbrev;
-
- if (pi->id_strlen > id_abbrev) {
- giterr_set(GITERR_PATCH,
- "The patch input contains %d id characters (cannot print %d)",
- id_abbrev, pi->id_strlen);
- return -1;
- }
-
- git_oid_tostr(start_oid, pi->id_strlen + 1, &delta->old_file.id);
- git_oid_tostr(end_oid, pi->id_strlen + 1, &delta->new_file.id);
-
- git_buf_printf(
- out, (pi->id_strlen <= GIT_OID_HEXSZ) ?
- ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
- delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
-
- if (delta->similarity > 0)
- git_buf_printf(out, "%03u", delta->similarity);
-
- if (delta->old_file.path != delta->new_file.path)
- git_buf_printf(
- out, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
- else
- git_buf_printf(
- out, "\t%s\n", delta->old_file.path ?
- delta->old_file.path : delta->new_file.path);
-
- if (git_buf_oom(out))
- return -1;
-
- pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
- pi->line.content = git_buf_cstr(out);
- pi->line.content_len = git_buf_len(out);
-
- return pi->print_cb(delta, NULL, &pi->line, pi->payload);
-}
-
-static int diff_print_modes(
- git_buf *out, const git_diff_delta *delta)
-{
- git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
- git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
-
- return git_buf_oom(out) ? -1 : 0;
-}
-
-static int diff_print_oid_range(
- git_buf *out, const git_diff_delta *delta, int id_strlen)
-{
- char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
-
- if (delta->old_file.mode &&
- id_strlen > delta->old_file.id_abbrev) {
- giterr_set(GITERR_PATCH,
- "The patch input contains %d id characters (cannot print %d)",
- delta->old_file.id_abbrev, id_strlen);
- return -1;
- }
-
- if ((delta->new_file.mode &&
- id_strlen > delta->new_file.id_abbrev)) {
- giterr_set(GITERR_PATCH,
- "The patch input contains %d id characters (cannot print %d)",
- delta->new_file.id_abbrev, id_strlen);
- return -1;
- }
-
- git_oid_tostr(start_oid, id_strlen + 1, &delta->old_file.id);
- git_oid_tostr(end_oid, id_strlen + 1, &delta->new_file.id);
-
- if (delta->old_file.mode == delta->new_file.mode) {
- git_buf_printf(out, "index %s..%s %o\n",
- start_oid, end_oid, delta->old_file.mode);
- } else {
- if (delta->old_file.mode == 0)
- git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
- else if (delta->new_file.mode == 0)
- git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
- else
- diff_print_modes(out, delta);
-
- git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
- }
-
- return git_buf_oom(out) ? -1 : 0;
-}
-
-static int diff_delta_format_path(
- git_buf *out, const char *prefix, const char *filename)
-{
- if (git_buf_joinpath(out, prefix, filename) < 0)
- return -1;
-
- return git_buf_quote(out);
-}
-
-static int diff_delta_format_with_paths(
- git_buf *out,
- const git_diff_delta *delta,
- const char *template,
- const char *oldpath,
- const char *newpath)
-{
- if (git_oid_iszero(&delta->old_file.id))
- oldpath = "/dev/null";
-
- if (git_oid_iszero(&delta->new_file.id))
- newpath = "/dev/null";
-
- return git_buf_printf(out, template, oldpath, newpath);
-}
-
-int diff_delta_format_similarity_header(
- git_buf *out,
- const git_diff_delta *delta)
-{
- git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
- const char *type;
- int error = 0;
-
- if (delta->similarity > 100) {
- giterr_set(GITERR_PATCH, "invalid similarity %d", delta->similarity);
- error = -1;
- goto done;
- }
-
- if (delta->status == GIT_DELTA_RENAMED)
- type = "rename";
- else if (delta->status == GIT_DELTA_COPIED)
- type = "copy";
- else
- abort();
-
- if ((error = git_buf_puts(&old_path, delta->old_file.path)) < 0 ||
- (error = git_buf_puts(&new_path, delta->new_file.path)) < 0 ||
- (error = git_buf_quote(&old_path)) < 0 ||
- (error = git_buf_quote(&new_path)) < 0)
- goto done;
-
- git_buf_printf(out,
- "similarity index %d%%\n"
- "%s from %s\n"
- "%s to %s\n",
- delta->similarity,
- type, old_path.ptr,
- type, new_path.ptr);
-
- if (git_buf_oom(out))
- error = -1;
-
-done:
- git_buf_free(&old_path);
- git_buf_free(&new_path);
-
- return error;
-}
-
-static bool delta_is_unchanged(const git_diff_delta *delta)
-{
- if (git_oid_iszero(&delta->old_file.id) &&
- git_oid_iszero(&delta->new_file.id))
- return true;
-
- if (delta->old_file.mode == GIT_FILEMODE_COMMIT ||
- delta->new_file.mode == GIT_FILEMODE_COMMIT)
- return false;
-
- if (git_oid_equal(&delta->old_file.id, &delta->new_file.id))
- return true;
-
- return false;
-}
-
-int git_diff_delta__format_file_header(
- git_buf *out,
- const git_diff_delta *delta,
- const char *oldpfx,
- const char *newpfx,
- int id_strlen)
-{
- git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
- bool unchanged = delta_is_unchanged(delta);
- int error = 0;
-
- if (!oldpfx)
- oldpfx = DIFF_OLD_PREFIX_DEFAULT;
- if (!newpfx)
- newpfx = DIFF_NEW_PREFIX_DEFAULT;
- if (!id_strlen)
- id_strlen = GIT_ABBREV_DEFAULT;
-
- if ((error = diff_delta_format_path(
- &old_path, oldpfx, delta->old_file.path)) < 0 ||
- (error = diff_delta_format_path(
- &new_path, newpfx, delta->new_file.path)) < 0)
- goto done;
-
- git_buf_clear(out);
-
- git_buf_printf(out, "diff --git %s %s\n",
- old_path.ptr, new_path.ptr);
-
- if (delta->status == GIT_DELTA_RENAMED ||
- (delta->status == GIT_DELTA_COPIED && unchanged)) {
- if ((error = diff_delta_format_similarity_header(out, delta)) < 0)
- goto done;
- }
-
- if (!unchanged) {
- if ((error = diff_print_oid_range(out, delta, id_strlen)) < 0)
- goto done;
-
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
- diff_delta_format_with_paths(out, delta,
- "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr);
- }
-
- if (unchanged && delta->old_file.mode != delta->new_file.mode)
- diff_print_modes(out, delta);
-
- if (git_buf_oom(out))
- error = -1;
-
-done:
- git_buf_free(&old_path);
- git_buf_free(&new_path);
-
- return error;
-}
-
-static int format_binary(
- diff_print_info *pi,
- git_diff_binary_t type,
- const char *data,
- size_t datalen,
- size_t inflatedlen)
-{
- const char *typename = type == GIT_DIFF_BINARY_DELTA ?
- "delta" : "literal";
- const char *scan, *end;
-
- git_buf_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen);
- pi->line.num_lines++;
-
- for (scan = data, end = data + datalen; scan < end; ) {
- size_t chunk_len = end - scan;
- if (chunk_len > 52)
- chunk_len = 52;
-
- if (chunk_len <= 26)
- git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1);
- else
- git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
-
- git_buf_encode_base85(pi->buf, scan, chunk_len);
- git_buf_putc(pi->buf, '\n');
-
- if (git_buf_oom(pi->buf))
- return -1;
-
- scan += chunk_len;
- pi->line.num_lines++;
- }
- git_buf_putc(pi->buf, '\n');
-
- return 0;
-}
-
-static int diff_print_patch_file_binary_noshow(
- diff_print_info *pi, git_diff_delta *delta,
- const char *old_pfx, const char *new_pfx)
-{
- git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
- int error;
-
- if ((error = diff_delta_format_path(
- &old_path, old_pfx, delta->old_file.path)) < 0 ||
- (error = diff_delta_format_path(
- &new_path, new_pfx, delta->new_file.path)) < 0)
- goto done;
-
- pi->line.num_lines = 1;
- error = diff_delta_format_with_paths(
- pi->buf, delta, "Binary files %s and %s differ\n",
- old_path.ptr, new_path.ptr);
-
-done:
- git_buf_free(&old_path);
- git_buf_free(&new_path);
-
- return error;
-}
-
-static int diff_print_patch_file_binary(
- diff_print_info *pi, git_diff_delta *delta,
- const char *old_pfx, const char *new_pfx,
- const git_diff_binary *binary)
-{
- size_t pre_binary_size;
- int error;
-
- if (delta->status == GIT_DELTA_UNMODIFIED)
- return 0;
-
- if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data)
- return diff_print_patch_file_binary_noshow(
- pi, delta, old_pfx, new_pfx);
-
- pre_binary_size = pi->buf->size;
- git_buf_printf(pi->buf, "GIT binary patch\n");
- pi->line.num_lines++;
-
- if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
- binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
- (error = format_binary(pi, binary->old_file.type, binary->old_file.data,
- binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
-
- if (error == GIT_EBUFS) {
- giterr_clear();
- git_buf_truncate(pi->buf, pre_binary_size);
-
- return diff_print_patch_file_binary_noshow(
- pi, delta, old_pfx, new_pfx);
- }
- }
-
- pi->line.num_lines++;
- return error;
-}
-
-static int diff_print_patch_file(
- const git_diff_delta *delta, float progress, void *data)
-{
- int error;
- diff_print_info *pi = data;
- const char *oldpfx =
- pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
- const char *newpfx =
- pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
-
- bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
- (pi->flags & GIT_DIFF_FORCE_BINARY);
- bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
- int id_strlen = pi->id_strlen;
-
- if (binary && show_binary)
- id_strlen = delta->old_file.id_abbrev ? delta->old_file.id_abbrev :
- delta->new_file.id_abbrev;
-
- GIT_UNUSED(progress);
-
- if (S_ISDIR(delta->new_file.mode) ||
- delta->status == GIT_DELTA_UNMODIFIED ||
- delta->status == GIT_DELTA_IGNORED ||
- delta->status == GIT_DELTA_UNREADABLE ||
- (delta->status == GIT_DELTA_UNTRACKED &&
- (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
- return 0;
-
- if ((error = git_diff_delta__format_file_header(
- pi->buf, delta, oldpfx, newpfx, id_strlen)) < 0)
- return error;
-
- pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
- pi->line.content = git_buf_cstr(pi->buf);
- pi->line.content_len = git_buf_len(pi->buf);
-
- return pi->print_cb(delta, NULL, &pi->line, pi->payload);
-}
-
-static int diff_print_patch_binary(
- const git_diff_delta *delta,
- const git_diff_binary *binary,
- void *data)
-{
- diff_print_info *pi = data;
- const char *old_pfx =
- pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
- const char *new_pfx =
- pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
- int error;
-
- git_buf_clear(pi->buf);
-
- if ((error = diff_print_patch_file_binary(
- pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0)
- return error;
-
- pi->line.origin = GIT_DIFF_LINE_BINARY;
- pi->line.content = git_buf_cstr(pi->buf);
- pi->line.content_len = git_buf_len(pi->buf);
-
- return pi->print_cb(delta, NULL, &pi->line, pi->payload);
-}
-
-static int diff_print_patch_hunk(
- const git_diff_delta *d,
- const git_diff_hunk *h,
- void *data)
-{
- diff_print_info *pi = data;
-
- if (S_ISDIR(d->new_file.mode))
- return 0;
-
- pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
- pi->line.content = h->header;
- pi->line.content_len = h->header_len;
-
- return pi->print_cb(d, h, &pi->line, pi->payload);
-}
-
-static int diff_print_patch_line(
- const git_diff_delta *delta,
- const git_diff_hunk *hunk,
- const git_diff_line *line,
- void *data)
-{
- diff_print_info *pi = data;
-
- if (S_ISDIR(delta->new_file.mode))
- return 0;
-
- return pi->print_cb(delta, hunk, line, pi->payload);
-}
-
-/* print a git_diff to an output callback */
-int git_diff_print(
- git_diff *diff,
- git_diff_format_t format,
- git_diff_line_cb print_cb,
- void *payload)
-{
- int error;
- git_buf buf = GIT_BUF_INIT;
- diff_print_info pi;
- git_diff_file_cb print_file = NULL;
- git_diff_binary_cb print_binary = NULL;
- git_diff_hunk_cb print_hunk = NULL;
- git_diff_line_cb print_line = NULL;
-
- switch (format) {
- case GIT_DIFF_FORMAT_PATCH:
- print_file = diff_print_patch_file;
- print_binary = diff_print_patch_binary;
- print_hunk = diff_print_patch_hunk;
- print_line = diff_print_patch_line;
- break;
- case GIT_DIFF_FORMAT_PATCH_HEADER:
- print_file = diff_print_patch_file;
- break;
- case GIT_DIFF_FORMAT_RAW:
- print_file = diff_print_one_raw;
- break;
- case GIT_DIFF_FORMAT_NAME_ONLY:
- print_file = diff_print_one_name_only;
- break;
- case GIT_DIFF_FORMAT_NAME_STATUS:
- print_file = diff_print_one_name_status;
- break;
- default:
- giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format);
- return -1;
- }
-
- if (!(error = diff_print_info_init_fromdiff(
- &pi, &buf, diff, format, print_cb, payload))) {
- error = git_diff_foreach(
- diff, print_file, print_binary, print_hunk, print_line, &pi);
-
- if (error) /* make sure error message is set */
- giterr_set_after_callback_function(error, "git_diff_print");
- }
-
- git_buf_free(&buf);
-
- return error;
-}
-
-int git_diff_print_callback__to_buf(
- const git_diff_delta *delta,
- const git_diff_hunk *hunk,
- const git_diff_line *line,
- void *payload)
-{
- git_buf *output = payload;
- GIT_UNUSED(delta); GIT_UNUSED(hunk);
-
- if (!output) {
- giterr_set(GITERR_INVALID, "Buffer pointer must be provided");
- return -1;
- }
-
- if (line->origin == GIT_DIFF_LINE_ADDITION ||
- line->origin == GIT_DIFF_LINE_DELETION ||
- line->origin == GIT_DIFF_LINE_CONTEXT)
- git_buf_putc(output, line->origin);
-
- return git_buf_put(output, line->content, line->content_len);
-}
-
-int git_diff_print_callback__to_file_handle(
- const git_diff_delta *delta,
- const git_diff_hunk *hunk,
- const git_diff_line *line,
- void *payload)
-{
- FILE *fp = payload ? payload : stdout;
-
- GIT_UNUSED(delta); GIT_UNUSED(hunk);
-
- if (line->origin == GIT_DIFF_LINE_CONTEXT ||
- line->origin == GIT_DIFF_LINE_ADDITION ||
- line->origin == GIT_DIFF_LINE_DELETION)
- fputc(line->origin, fp);
- fwrite(line->content, 1, line->content_len, fp);
- return 0;
-}
-
-/* print a git_diff to a git_buf */
-int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format)
-{
- assert(out && diff);
- git_buf_sanitize(out);
- return git_diff_print(
- diff, format, git_diff_print_callback__to_buf, out);
-}
-
-/* print a git_patch to an output callback */
-int git_patch_print(
- git_patch *patch,
- git_diff_line_cb print_cb,
- void *payload)
-{
- int error;
- git_buf temp = GIT_BUF_INIT;
- diff_print_info pi;
-
- assert(patch && print_cb);
-
- if (!(error = diff_print_info_init_frompatch(
- &pi, &temp, patch,
- GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
- {
- error = git_patch__invoke_callbacks(
- patch,
- diff_print_patch_file, diff_print_patch_binary,
- diff_print_patch_hunk, diff_print_patch_line,
- &pi);
-
- if (error) /* make sure error message is set */
- giterr_set_after_callback_function(error, "git_patch_print");
- }
-
- git_buf_free(&temp);
-
- return error;
-}
-
-/* print a git_patch to a git_buf */
-int git_patch_to_buf(git_buf *out, git_patch *patch)
-{
- assert(out && patch);
- git_buf_sanitize(out);
- return git_patch_print(patch, git_diff_print_callback__to_buf, out);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "vector.h"
-#include "diff.h"
-#include "patch_generate.h"
-
-#define DIFF_RENAME_FILE_SEPARATOR " => "
-#define STATS_FULL_MIN_SCALE 7
-
-typedef struct {
- size_t insertions;
- size_t deletions;
-} diff_file_stats;
-
-struct git_diff_stats {
- git_diff *diff;
- diff_file_stats *filestats;
-
- size_t files_changed;
- size_t insertions;
- size_t deletions;
- size_t renames;
-
- size_t max_name;
- size_t max_filestat;
- int max_digits;
-};
-
-static int digits_for_value(size_t val)
-{
- int count = 1;
- size_t placevalue = 10;
-
- while (val >= placevalue) {
- ++count;
- placevalue *= 10;
- }
-
- return count;
-}
-
-int git_diff_file_stats__full_to_buf(
- git_buf *out,
- const git_diff_delta *delta,
- const diff_file_stats *filestat,
- const git_diff_stats *stats,
- size_t width)
-{
- const char *old_path = NULL, *new_path = NULL;
- size_t padding, old_size, new_size;
-
- old_path = delta->old_file.path;
- new_path = delta->new_file.path;
- old_size = delta->old_file.size;
- new_size = delta->new_file.size;
-
- if (git_buf_printf(out, " %s", old_path) < 0)
- goto on_error;
-
- if (strcmp(old_path, new_path) != 0) {
- padding = stats->max_name - strlen(old_path) - strlen(new_path);
-
- if (git_buf_printf(out, DIFF_RENAME_FILE_SEPARATOR "%s", new_path) < 0)
- goto on_error;
- } else {
- padding = stats->max_name - strlen(old_path);
-
- if (stats->renames > 0)
- padding += strlen(DIFF_RENAME_FILE_SEPARATOR);
- }
-
- if (git_buf_putcn(out, ' ', padding) < 0 ||
- git_buf_puts(out, " | ") < 0)
- goto on_error;
-
- if (delta->flags & GIT_DIFF_FLAG_BINARY) {
- if (git_buf_printf(out,
- "Bin %" PRIuZ " -> %" PRIuZ " bytes", old_size, new_size) < 0)
- goto on_error;
- }
- else {
- if (git_buf_printf(out,
- "%*" PRIuZ, stats->max_digits,
- filestat->insertions + filestat->deletions) < 0)
- goto on_error;
-
- if (filestat->insertions || filestat->deletions) {
- if (git_buf_putc(out, ' ') < 0)
- goto on_error;
-
- if (!width) {
- if (git_buf_putcn(out, '+', filestat->insertions) < 0 ||
- git_buf_putcn(out, '-', filestat->deletions) < 0)
- goto on_error;
- } else {
- size_t total = filestat->insertions + filestat->deletions;
- size_t full = (total * width + stats->max_filestat / 2) /
- stats->max_filestat;
- size_t plus = full * filestat->insertions / total;
- size_t minus = full - plus;
-
- if (git_buf_putcn(out, '+', max(plus, 1)) < 0 ||
- git_buf_putcn(out, '-', max(minus, 1)) < 0)
- goto on_error;
- }
- }
- }
-
- git_buf_putc(out, '\n');
-
-on_error:
- return (git_buf_oom(out) ? -1 : 0);
-}
-
-int git_diff_file_stats__number_to_buf(
- git_buf *out,
- const git_diff_delta *delta,
- const diff_file_stats *filestats)
-{
- int error;
- const char *path = delta->new_file.path;
-
- if (delta->flags & GIT_DIFF_FLAG_BINARY)
- error = git_buf_printf(out, "%-8c" "%-8c" "%s\n", '-', '-', path);
- else
- error = git_buf_printf(out, "%-8" PRIuZ "%-8" PRIuZ "%s\n",
- filestats->insertions, filestats->deletions, path);
-
- return error;
-}
-
-int git_diff_file_stats__summary_to_buf(
- git_buf *out,
- const git_diff_delta *delta)
-{
- if (delta->old_file.mode != delta->new_file.mode) {
- if (delta->old_file.mode == 0) {
- git_buf_printf(out, " create mode %06o %s\n",
- delta->new_file.mode, delta->new_file.path);
- }
- else if (delta->new_file.mode == 0) {
- git_buf_printf(out, " delete mode %06o %s\n",
- delta->old_file.mode, delta->old_file.path);
- }
- else {
- git_buf_printf(out, " mode change %06o => %06o %s\n",
- delta->old_file.mode, delta->new_file.mode, delta->new_file.path);
- }
- }
-
- return 0;
-}
-
-int git_diff_get_stats(
- git_diff_stats **out,
- git_diff *diff)
-{
- size_t i, deltas;
- size_t total_insertions = 0, total_deletions = 0;
- git_diff_stats *stats = NULL;
- int error = 0;
-
- assert(out && diff);
-
- stats = git__calloc(1, sizeof(git_diff_stats));
- GITERR_CHECK_ALLOC(stats);
-
- deltas = git_diff_num_deltas(diff);
-
- stats->filestats = git__calloc(deltas, sizeof(diff_file_stats));
- if (!stats->filestats) {
- git__free(stats);
- return -1;
- }
-
- stats->diff = diff;
- GIT_REFCOUNT_INC(diff);
-
- for (i = 0; i < deltas && !error; ++i) {
- git_patch *patch = NULL;
- size_t add = 0, remove = 0, namelen;
- const git_diff_delta *delta;
-
- if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
- break;
-
- /* keep a count of renames because it will affect formatting */
- delta = patch->delta;
-
- /* TODO ugh */
- namelen = strlen(delta->new_file.path);
- if (strcmp(delta->old_file.path, delta->new_file.path) != 0) {
- namelen += strlen(delta->old_file.path);
- stats->renames++;
- }
-
- /* and, of course, count the line stats */
- error = git_patch_line_stats(NULL, &add, &remove, patch);
-
- git_patch_free(patch);
-
- stats->filestats[i].insertions = add;
- stats->filestats[i].deletions = remove;
-
- total_insertions += add;
- total_deletions += remove;
-
- if (stats->max_name < namelen)
- stats->max_name = namelen;
- if (stats->max_filestat < add + remove)
- stats->max_filestat = add + remove;
- }
-
- stats->files_changed = deltas;
- stats->insertions = total_insertions;
- stats->deletions = total_deletions;
- stats->max_digits = digits_for_value(stats->max_filestat + 1);
-
- if (error < 0) {
- git_diff_stats_free(stats);
- stats = NULL;
- }
-
- *out = stats;
- return error;
-}
-
-size_t git_diff_stats_files_changed(
- const git_diff_stats *stats)
-{
- assert(stats);
-
- return stats->files_changed;
-}
-
-size_t git_diff_stats_insertions(
- const git_diff_stats *stats)
-{
- assert(stats);
-
- return stats->insertions;
-}
-
-size_t git_diff_stats_deletions(
- const git_diff_stats *stats)
-{
- assert(stats);
-
- return stats->deletions;
-}
-
-int git_diff_stats_to_buf(
- git_buf *out,
- const git_diff_stats *stats,
- git_diff_stats_format_t format,
- size_t width)
-{
- int error = 0;
- size_t i;
- const git_diff_delta *delta;
-
- assert(out && stats);
-
- if (format & GIT_DIFF_STATS_NUMBER) {
- for (i = 0; i < stats->files_changed; ++i) {
- if ((delta = git_diff_get_delta(stats->diff, i)) == NULL)
- continue;
-
- error = git_diff_file_stats__number_to_buf(
- out, delta, &stats->filestats[i]);
- if (error < 0)
- return error;
- }
- }
-
- if (format & GIT_DIFF_STATS_FULL) {
- if (width > 0) {
- if (width > stats->max_name + stats->max_digits + 5)
- width -= (stats->max_name + stats->max_digits + 5);
- if (width < STATS_FULL_MIN_SCALE)
- width = STATS_FULL_MIN_SCALE;
- }
- if (width > stats->max_filestat)
- width = 0;
-
- for (i = 0; i < stats->files_changed; ++i) {
- if ((delta = git_diff_get_delta(stats->diff, i)) == NULL)
- continue;
-
- error = git_diff_file_stats__full_to_buf(
- out, delta, &stats->filestats[i], stats, width);
- if (error < 0)
- return error;
- }
- }
-
- if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) {
- git_buf_printf(
- out, " %" PRIuZ " file%s changed",
- stats->files_changed, stats->files_changed != 1 ? "s" : "");
-
- if (stats->insertions || stats->deletions == 0)
- git_buf_printf(
- out, ", %" PRIuZ " insertion%s(+)",
- stats->insertions, stats->insertions != 1 ? "s" : "");
-
- if (stats->deletions || stats->insertions == 0)
- git_buf_printf(
- out, ", %" PRIuZ " deletion%s(-)",
- stats->deletions, stats->deletions != 1 ? "s" : "");
-
- git_buf_putc(out, '\n');
-
- if (git_buf_oom(out))
- return -1;
- }
-
- if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) {
- for (i = 0; i < stats->files_changed; ++i) {
- if ((delta = git_diff_get_delta(stats->diff, i)) == NULL)
- continue;
-
- error = git_diff_file_stats__summary_to_buf(out, delta);
- if (error < 0)
- return error;
- }
- }
-
- return error;
-}
-
-void git_diff_stats_free(git_diff_stats *stats)
-{
- if (stats == NULL)
- return;
-
- git_diff_free(stats->diff); /* bumped refcount in constructor */
- git__free(stats->filestats);
- git__free(stats);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-
-#include "git2/config.h"
-#include "git2/blob.h"
-#include "git2/sys/hashsig.h"
-
-#include "diff.h"
-#include "diff_generate.h"
-#include "path.h"
-#include "fileops.h"
-#include "config.h"
-
-git_diff_delta *git_diff__delta_dup(
- const git_diff_delta *d, git_pool *pool)
-{
- git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
- if (!delta)
- return NULL;
-
- memcpy(delta, d, sizeof(git_diff_delta));
- GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags);
-
- if (d->old_file.path != NULL) {
- delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
- if (delta->old_file.path == NULL)
- goto fail;
- }
-
- if (d->new_file.path != d->old_file.path && d->new_file.path != NULL) {
- delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
- if (delta->new_file.path == NULL)
- goto fail;
- } else {
- delta->new_file.path = delta->old_file.path;
- }
-
- return delta;
-
-fail:
- git__free(delta);
- return NULL;
-}
-
-git_diff_delta *git_diff__merge_like_cgit(
- const git_diff_delta *a,
- const git_diff_delta *b,
- git_pool *pool)
-{
- git_diff_delta *dup;
-
- /* Emulate C git for merging two diffs (a la 'git diff <sha>').
- *
- * When C git does a diff between the work dir and a tree, it actually
- * diffs with the index but uses the workdir contents. This emulates
- * those choices so we can emulate the type of diff.
- *
- * We have three file descriptions here, let's call them:
- * f1 = a->old_file
- * f2 = a->new_file AND b->old_file
- * f3 = b->new_file
- */
-
- /* If one of the diffs is a conflict, just dup it */
- if (b->status == GIT_DELTA_CONFLICTED)
- return git_diff__delta_dup(b, pool);
- if (a->status == GIT_DELTA_CONFLICTED)
- return git_diff__delta_dup(a, pool);
-
- /* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */
- if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED)
- return git_diff__delta_dup(a, pool);
-
- /* otherwise, base this diff on the 'b' diff */
- if ((dup = git_diff__delta_dup(b, pool)) == NULL)
- return NULL;
-
- /* If 'a' status is uninteresting, then we're done */
- if (a->status == GIT_DELTA_UNMODIFIED ||
- a->status == GIT_DELTA_UNTRACKED ||
- a->status == GIT_DELTA_UNREADABLE)
- return dup;
-
- assert(b->status != GIT_DELTA_UNMODIFIED);
-
- /* A cgit exception is that the diff of a file that is only in the
- * index (i.e. not in HEAD nor workdir) is given as empty.
- */
- if (dup->status == GIT_DELTA_DELETED) {
- if (a->status == GIT_DELTA_ADDED) {
- dup->status = GIT_DELTA_UNMODIFIED;
- dup->nfiles = 2;
- }
- /* else don't overwrite DELETE status */
- } else {
- dup->status = a->status;
- dup->nfiles = a->nfiles;
- }
-
- git_oid_cpy(&dup->old_file.id, &a->old_file.id);
- dup->old_file.mode = a->old_file.mode;
- dup->old_file.size = a->old_file.size;
- dup->old_file.flags = a->old_file.flags;
-
- return dup;
-}
-
-int git_diff__merge(
- git_diff *onto, const git_diff *from, git_diff__merge_cb cb)
-{
- int error = 0;
- git_pool onto_pool;
- git_vector onto_new;
- git_diff_delta *delta;
- bool ignore_case, reversed;
- unsigned int i, j;
-
- assert(onto && from);
-
- if (!from->deltas.length)
- return 0;
-
- ignore_case = ((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0);
- reversed = ((onto->opts.flags & GIT_DIFF_REVERSE) != 0);
-
- if (ignore_case != ((from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0) ||
- reversed != ((from->opts.flags & GIT_DIFF_REVERSE) != 0)) {
- giterr_set(GITERR_INVALID,
- "Attempt to merge diffs created with conflicting options");
- return -1;
- }
-
- if (git_vector_init(&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0)
- return -1;
-
- git_pool_init(&onto_pool, 1);
-
- for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
- git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
- const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
- int cmp = !f ? -1 : !o ? 1 :
- STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
-
- if (cmp < 0) {
- delta = git_diff__delta_dup(o, &onto_pool);
- i++;
- } else if (cmp > 0) {
- delta = git_diff__delta_dup(f, &onto_pool);
- j++;
- } else {
- const git_diff_delta *left = reversed ? f : o;
- const git_diff_delta *right = reversed ? o : f;
-
- delta = cb(left, right, &onto_pool);
- i++;
- j++;
- }
-
- /* the ignore rules for the target may not match the source
- * or the result of a merged delta could be skippable...
- */
- if (delta && git_diff_delta__should_skip(&onto->opts, delta)) {
- git__free(delta);
- continue;
- }
-
- if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
- break;
- }
-
- if (!error) {
- git_vector_swap(&onto->deltas, &onto_new);
- git_pool_swap(&onto->pool, &onto_pool);
-
- if ((onto->opts.flags & GIT_DIFF_REVERSE) != 0)
- onto->old_src = from->old_src;
- else
- onto->new_src = from->new_src;
-
- /* prefix strings also come from old pool, so recreate those.*/
- onto->opts.old_prefix =
- git_pool_strdup_safe(&onto->pool, onto->opts.old_prefix);
- onto->opts.new_prefix =
- git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix);
- }
-
- git_vector_free_deep(&onto_new);
- git_pool_clear(&onto_pool);
-
- return error;
-}
-
-int git_diff_merge(git_diff *onto, const git_diff *from)
-{
- return git_diff__merge(onto, from, git_diff__merge_like_cgit);
-}
-
-int git_diff_find_similar__hashsig_for_file(
- void **out, const git_diff_file *f, const char *path, void *p)
-{
- git_hashsig_option_t opt = (git_hashsig_option_t)(intptr_t)p;
-
- GIT_UNUSED(f);
- return git_hashsig_create_fromfile((git_hashsig **)out, path, opt);
-}
-
-int git_diff_find_similar__hashsig_for_buf(
- void **out, const git_diff_file *f, const char *buf, size_t len, void *p)
-{
- git_hashsig_option_t opt = (git_hashsig_option_t)(intptr_t)p;
-
- GIT_UNUSED(f);
- return git_hashsig_create((git_hashsig **)out, buf, len, opt);
-}
-
-void git_diff_find_similar__hashsig_free(void *sig, void *payload)
-{
- GIT_UNUSED(payload);
- git_hashsig_free(sig);
-}
-
-int git_diff_find_similar__calc_similarity(
- int *score, void *siga, void *sigb, void *payload)
-{
- int error;
-
- GIT_UNUSED(payload);
- error = git_hashsig_compare(siga, sigb);
- if (error < 0)
- return error;
-
- *score = error;
- return 0;
-}
-
-#define DEFAULT_THRESHOLD 50
-#define DEFAULT_BREAK_REWRITE_THRESHOLD 60
-#define DEFAULT_RENAME_LIMIT 200
-
-static int normalize_find_opts(
- git_diff *diff,
- git_diff_find_options *opts,
- const git_diff_find_options *given)
-{
- git_config *cfg = NULL;
- git_hashsig_option_t hashsig_opts;
-
- GITERR_CHECK_VERSION(given, GIT_DIFF_FIND_OPTIONS_VERSION, "git_diff_find_options");
-
- if (diff->repo != NULL &&
- git_repository_config__weakptr(&cfg, diff->repo) < 0)
- return -1;
-
- if (given)
- memcpy(opts, given, sizeof(*opts));
-
- if (!given ||
- (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG)
- {
- if (cfg) {
- char *rule =
- git_config__get_string_force(cfg, "diff.renames", "true");
- int boolval;
-
- if (!git__parse_bool(&boolval, rule) && !boolval)
- /* don't set FIND_RENAMES if bool value is false */;
- else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy"))
- opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
- else
- opts->flags |= GIT_DIFF_FIND_RENAMES;
-
- git__free(rule);
- } else {
- /* set default flag */
- opts->flags |= GIT_DIFF_FIND_RENAMES;
- }
- }
-
- /* some flags imply others */
-
- if (opts->flags & GIT_DIFF_FIND_EXACT_MATCH_ONLY) {
- /* if we are only looking for exact matches, then don't turn
- * MODIFIED items into ADD/DELETE pairs because it's too picky
- */
- opts->flags &= ~(GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES);
-
- /* similarly, don't look for self-rewrites to split */
- opts->flags &= ~GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
- }
-
- if (opts->flags & GIT_DIFF_FIND_RENAMES_FROM_REWRITES)
- opts->flags |= GIT_DIFF_FIND_RENAMES;
-
- if (opts->flags & GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)
- opts->flags |= GIT_DIFF_FIND_COPIES;
-
- if (opts->flags & GIT_DIFF_BREAK_REWRITES)
- opts->flags |= GIT_DIFF_FIND_REWRITES;
-
-#define USE_DEFAULT(X) ((X) == 0 || (X) > 100)
-
- if (USE_DEFAULT(opts->rename_threshold))
- opts->rename_threshold = DEFAULT_THRESHOLD;
-
- if (USE_DEFAULT(opts->rename_from_rewrite_threshold))
- opts->rename_from_rewrite_threshold = DEFAULT_THRESHOLD;
-
- if (USE_DEFAULT(opts->copy_threshold))
- opts->copy_threshold = DEFAULT_THRESHOLD;
-
- if (USE_DEFAULT(opts->break_rewrite_threshold))
- opts->break_rewrite_threshold = DEFAULT_BREAK_REWRITE_THRESHOLD;
-
-#undef USE_DEFAULT
-
- if (!opts->rename_limit) {
- if (cfg) {
- opts->rename_limit = git_config__get_int_force(
- cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT);
- }
-
- if (opts->rename_limit <= 0)
- opts->rename_limit = DEFAULT_RENAME_LIMIT;
- }
-
- /* assign the internal metric with whitespace flag as payload */
- if (!opts->metric) {
- opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
- GITERR_CHECK_ALLOC(opts->metric);
-
- opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
- opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
- opts->metric->free_signature = git_diff_find_similar__hashsig_free;
- opts->metric->similarity = git_diff_find_similar__calc_similarity;
-
- if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE)
- hashsig_opts = GIT_HASHSIG_IGNORE_WHITESPACE;
- else if (opts->flags & GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE)
- hashsig_opts = GIT_HASHSIG_NORMAL;
- else
- hashsig_opts = GIT_HASHSIG_SMART_WHITESPACE;
- hashsig_opts |= GIT_HASHSIG_ALLOW_SMALL_FILES;
- opts->metric->payload = (void *)hashsig_opts;
- }
-
- return 0;
-}
-
-static int insert_delete_side_of_split(
- git_diff *diff, git_vector *onto, const git_diff_delta *delta)
-{
- /* make new record for DELETED side of split */
- git_diff_delta *deleted = git_diff__delta_dup(delta, &diff->pool);
- GITERR_CHECK_ALLOC(deleted);
-
- deleted->status = GIT_DELTA_DELETED;
- deleted->nfiles = 1;
- memset(&deleted->new_file, 0, sizeof(deleted->new_file));
- deleted->new_file.path = deleted->old_file.path;
- deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
-
- return git_vector_insert(onto, deleted);
-}
-
-static int apply_splits_and_deletes(
- git_diff *diff, size_t expected_size, bool actually_split)
-{
- git_vector onto = GIT_VECTOR_INIT;
- size_t i;
- git_diff_delta *delta;
-
- if (git_vector_init(&onto, expected_size, git_diff_delta__cmp) < 0)
- return -1;
-
- /* build new delta list without TO_DELETE and splitting TO_SPLIT */
- git_vector_foreach(&diff->deltas, i, delta) {
- if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
- continue;
-
- if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0 && actually_split) {
- delta->similarity = 0;
-
- if (insert_delete_side_of_split(diff, &onto, delta) < 0)
- goto on_error;
-
- if (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR)
- delta->status = GIT_DELTA_UNTRACKED;
- else
- delta->status = GIT_DELTA_ADDED;
- delta->nfiles = 1;
- memset(&delta->old_file, 0, sizeof(delta->old_file));
- delta->old_file.path = delta->new_file.path;
- delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
- }
-
- /* clean up delta before inserting into new list */
- GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags);
-
- if (delta->status != GIT_DELTA_COPIED &&
- delta->status != GIT_DELTA_RENAMED &&
- (delta->status != GIT_DELTA_MODIFIED || actually_split))
- delta->similarity = 0;
-
- /* insert into new list */
- if (git_vector_insert(&onto, delta) < 0)
- goto on_error;
- }
-
- /* cannot return an error past this point */
-
- /* free deltas from old list that didn't make it to the new one */
- git_vector_foreach(&diff->deltas, i, delta) {
- if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
- git__free(delta);
- }
-
- /* swap new delta list into place */
- git_vector_swap(&diff->deltas, &onto);
- git_vector_free(&onto);
- git_vector_sort(&diff->deltas);
-
- return 0;
-
-on_error:
- git_vector_free_deep(&onto);
-
- return -1;
-}
-
-GIT_INLINE(git_diff_file *) similarity_get_file(git_diff *diff, size_t idx)
-{
- git_diff_delta *delta = git_vector_get(&diff->deltas, idx / 2);
- return (idx & 1) ? &delta->new_file : &delta->old_file;
-}
-
-typedef struct {
- size_t idx;
- git_iterator_type_t src;
- git_repository *repo;
- git_diff_file *file;
- git_buf data;
- git_odb_object *odb_obj;
- git_blob *blob;
-} similarity_info;
-
-static int similarity_init(
- similarity_info *info, git_diff *diff, size_t file_idx)
-{
- info->idx = file_idx;
- info->src = (file_idx & 1) ? diff->new_src : diff->old_src;
- info->repo = diff->repo;
- info->file = similarity_get_file(diff, file_idx);
- info->odb_obj = NULL;
- info->blob = NULL;
- git_buf_init(&info->data, 0);
-
- if (info->file->size > 0 || info->src == GIT_ITERATOR_TYPE_WORKDIR)
- return 0;
-
- return git_diff_file__resolve_zero_size(
- info->file, &info->odb_obj, info->repo);
-}
-
-static int similarity_sig(
- similarity_info *info,
- const git_diff_find_options *opts,
- void **cache)
-{
- int error = 0;
- git_diff_file *file = info->file;
-
- if (info->src == GIT_ITERATOR_TYPE_WORKDIR) {
- if ((error = git_buf_joinpath(
- &info->data, git_repository_workdir(info->repo), file->path)) < 0)
- return error;
-
- /* if path is not a regular file, just skip this item */
- if (!git_path_isfile(info->data.ptr))
- return 0;
-
- /* TODO: apply wd-to-odb filters to file data if necessary */
-
- error = opts->metric->file_signature(
- &cache[info->idx], info->file,
- info->data.ptr, opts->metric->payload);
- } else {
- /* if we didn't initially know the size, we might have an odb_obj
- * around from earlier, so convert that, otherwise load the blob now
- */
- if (info->odb_obj != NULL)
- error = git_object__from_odb_object(
- (git_object **)&info->blob, info->repo,
- info->odb_obj, GIT_OBJ_BLOB);
- else
- error = git_blob_lookup(&info->blob, info->repo, &file->id);
-
- if (error < 0) {
- /* if lookup fails, just skip this item in similarity calc */
- giterr_clear();
- } else {
- size_t sz;
-
- /* index size may not be actual blob size if filtered */
- if (file->size != git_blob_rawsize(info->blob))
- file->size = git_blob_rawsize(info->blob);
-
- sz = (size_t)(git__is_sizet(file->size) ? file->size : -1);
-
- error = opts->metric->buffer_signature(
- &cache[info->idx], info->file,
- git_blob_rawcontent(info->blob), sz, opts->metric->payload);
- }
- }
-
- return error;
-}
-
-static void similarity_unload(similarity_info *info)
-{
- if (info->odb_obj)
- git_odb_object_free(info->odb_obj);
-
- if (info->blob)
- git_blob_free(info->blob);
- else
- git_buf_free(&info->data);
-}
-
-#define FLAG_SET(opts,flag_name) (((opts)->flags & flag_name) != 0)
-
-/* - score < 0 means files cannot be compared
- * - score >= 100 means files are exact match
- * - score == 0 means files are completely different
- */
-static int similarity_measure(
- int *score,
- git_diff *diff,
- const git_diff_find_options *opts,
- void **cache,
- size_t a_idx,
- size_t b_idx)
-{
- git_diff_file *a_file = similarity_get_file(diff, a_idx);
- git_diff_file *b_file = similarity_get_file(diff, b_idx);
- bool exact_match = FLAG_SET(opts, GIT_DIFF_FIND_EXACT_MATCH_ONLY);
- int error = 0;
- similarity_info a_info, b_info;
-
- *score = -1;
-
- /* don't try to compare files of different types */
- if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode))
- return 0;
-
- /* if exact match is requested, force calculation of missing OIDs now */
- if (exact_match) {
- if (git_oid_iszero(&a_file->id) &&
- diff->old_src == GIT_ITERATOR_TYPE_WORKDIR &&
- !git_diff__oid_for_file(&a_file->id,
- diff, a_file->path, a_file->mode, a_file->size))
- a_file->flags |= GIT_DIFF_FLAG_VALID_ID;
-
- if (git_oid_iszero(&b_file->id) &&
- diff->new_src == GIT_ITERATOR_TYPE_WORKDIR &&
- !git_diff__oid_for_file(&b_file->id,
- diff, b_file->path, b_file->mode, b_file->size))
- b_file->flags |= GIT_DIFF_FLAG_VALID_ID;
- }
-
- /* check OID match as a quick test */
- if (git_oid__cmp(&a_file->id, &b_file->id) == 0) {
- *score = 100;
- return 0;
- }
-
- /* don't calculate signatures if we are doing exact match */
- if (exact_match) {
- *score = 0;
- return 0;
- }
-
- memset(&a_info, 0, sizeof(a_info));
- memset(&b_info, 0, sizeof(b_info));
-
- /* set up similarity data (will try to update missing file sizes) */
- if (!cache[a_idx] && (error = similarity_init(&a_info, diff, a_idx)) < 0)
- return error;
- if (!cache[b_idx] && (error = similarity_init(&b_info, diff, b_idx)) < 0)
- goto cleanup;
-
- /* check if file sizes are nowhere near each other */
- if (a_file->size > 127 &&
- b_file->size > 127 &&
- (a_file->size > (b_file->size << 3) ||
- b_file->size > (a_file->size << 3)))
- goto cleanup;
-
- /* update signature cache if needed */
- if (!cache[a_idx]) {
- if ((error = similarity_sig(&a_info, opts, cache)) < 0)
- goto cleanup;
- }
- if (!cache[b_idx]) {
- if ((error = similarity_sig(&b_info, opts, cache)) < 0)
- goto cleanup;
- }
-
- /* calculate similarity provided that the metric choose to process
- * both the a and b files (some may not if file is too big, etc).
- */
- if (cache[a_idx] && cache[b_idx])
- error = opts->metric->similarity(
- score, cache[a_idx], cache[b_idx], opts->metric->payload);
-
-cleanup:
- similarity_unload(&a_info);
- similarity_unload(&b_info);
-
- return error;
-}
-
-static int calc_self_similarity(
- git_diff *diff,
- const git_diff_find_options *opts,
- size_t delta_idx,
- void **cache)
-{
- int error, similarity = -1;
- git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx);
-
- if ((delta->flags & GIT_DIFF_FLAG__HAS_SELF_SIMILARITY) != 0)
- return 0;
-
- error = similarity_measure(
- &similarity, diff, opts, cache, 2 * delta_idx, 2 * delta_idx + 1);
- if (error < 0)
- return error;
-
- if (similarity >= 0) {
- delta->similarity = (uint16_t)similarity;
- delta->flags |= GIT_DIFF_FLAG__HAS_SELF_SIMILARITY;
- }
-
- return 0;
-}
-
-static bool is_rename_target(
- git_diff *diff,
- const git_diff_find_options *opts,
- size_t delta_idx,
- void **cache)
-{
- git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx);
-
- /* skip things that aren't plain blobs */
- if (!GIT_MODE_ISBLOB(delta->new_file.mode))
- return false;
-
- /* only consider ADDED, RENAMED, COPIED, and split MODIFIED as
- * targets; maybe include UNTRACKED if requested.
- */
- switch (delta->status) {
- case GIT_DELTA_UNMODIFIED:
- case GIT_DELTA_DELETED:
- case GIT_DELTA_IGNORED:
- case GIT_DELTA_CONFLICTED:
- return false;
-
- case GIT_DELTA_MODIFIED:
- if (!FLAG_SET(opts, GIT_DIFF_FIND_REWRITES) &&
- !FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES))
- return false;
-
- if (calc_self_similarity(diff, opts, delta_idx, cache) < 0)
- return false;
-
- if (FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES) &&
- delta->similarity < opts->break_rewrite_threshold) {
- delta->flags |= GIT_DIFF_FLAG__TO_SPLIT;
- break;
- }
- if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
- delta->similarity < opts->rename_from_rewrite_threshold)
- break;
-
- return false;
-
- case GIT_DELTA_UNTRACKED:
- if (!FLAG_SET(opts, GIT_DIFF_FIND_FOR_UNTRACKED))
- return false;
- break;
-
- default: /* all other status values should be checked */
- break;
- }
-
- delta->flags |= GIT_DIFF_FLAG__IS_RENAME_TARGET;
- return true;
-}
-
-static bool is_rename_source(
- git_diff *diff,
- const git_diff_find_options *opts,
- size_t delta_idx,
- void **cache)
-{
- git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx);
-
- /* skip things that aren't blobs */
- if (!GIT_MODE_ISBLOB(delta->old_file.mode))
- return false;
-
- switch (delta->status) {
- case GIT_DELTA_ADDED:
- case GIT_DELTA_UNTRACKED:
- case GIT_DELTA_UNREADABLE:
- case GIT_DELTA_IGNORED:
- case GIT_DELTA_CONFLICTED:
- return false;
-
- case GIT_DELTA_DELETED:
- case GIT_DELTA_TYPECHANGE:
- break;
-
- case GIT_DELTA_UNMODIFIED:
- if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED))
- return false;
- if (FLAG_SET(opts, GIT_DIFF_FIND_REMOVE_UNMODIFIED))
- delta->flags |= GIT_DIFF_FLAG__TO_DELETE;
- break;
-
- default: /* MODIFIED, RENAMED, COPIED */
- /* if we're finding copies, this could be a source */
- if (FLAG_SET(opts, GIT_DIFF_FIND_COPIES))
- break;
-
- /* otherwise, this is only a source if we can split it */
- if (!FLAG_SET(opts, GIT_DIFF_FIND_REWRITES) &&
- !FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES))
- return false;
-
- if (calc_self_similarity(diff, opts, delta_idx, cache) < 0)
- return false;
-
- if (FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES) &&
- delta->similarity < opts->break_rewrite_threshold) {
- delta->flags |= GIT_DIFF_FLAG__TO_SPLIT;
- break;
- }
-
- if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
- delta->similarity < opts->rename_from_rewrite_threshold)
- break;
-
- return false;
- }
-
- delta->flags |= GIT_DIFF_FLAG__IS_RENAME_SOURCE;
- return true;
-}
-
-GIT_INLINE(bool) delta_is_split(git_diff_delta *delta)
-{
- return (delta->status == GIT_DELTA_TYPECHANGE ||
- (delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0);
-}
-
-GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
-{
- return (delta->status == GIT_DELTA_ADDED ||
- delta->status == GIT_DELTA_UNTRACKED ||
- delta->status == GIT_DELTA_UNREADABLE ||
- delta->status == GIT_DELTA_IGNORED);
-}
-
-GIT_INLINE(void) delta_make_rename(
- git_diff_delta *to, const git_diff_delta *from, uint16_t similarity)
-{
- to->status = GIT_DELTA_RENAMED;
- to->similarity = similarity;
- to->nfiles = 2;
- memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
- to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
-}
-
-typedef struct {
- size_t idx;
- uint16_t similarity;
-} diff_find_match;
-
-int git_diff_find_similar(
- git_diff *diff,
- const git_diff_find_options *given_opts)
-{
- size_t s, t;
- int error = 0, result;
- uint16_t similarity;
- git_diff_delta *src, *tgt;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- size_t num_deltas, num_srcs = 0, num_tgts = 0;
- size_t tried_srcs = 0, tried_tgts = 0;
- size_t num_rewrites = 0, num_updates = 0, num_bumped = 0;
- size_t sigcache_size;
- void **sigcache = NULL; /* cache of similarity metric file signatures */
- diff_find_match *tgt2src = NULL;
- diff_find_match *src2tgt = NULL;
- diff_find_match *tgt2src_copy = NULL;
- diff_find_match *best_match;
- git_diff_file swap;
-
- if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
- return error;
-
- num_deltas = diff->deltas.length;
-
- /* TODO: maybe abort if deltas.length > rename_limit ??? */
- if (!git__is_uint32(num_deltas))
- goto cleanup;
-
- /* No flags set; nothing to do */
- if ((opts.flags & GIT_DIFF_FIND_ALL) == 0)
- goto cleanup;
-
- GITERR_CHECK_ALLOC_MULTIPLY(&sigcache_size, num_deltas, 2);
- sigcache = git__calloc(sigcache_size, sizeof(void *));
- GITERR_CHECK_ALLOC(sigcache);
-
- /* Label rename sources and targets
- *
- * This will also set self-similarity scores for MODIFIED files and
- * mark them for splitting if break-rewrites is enabled
- */
- git_vector_foreach(&diff->deltas, t, tgt) {
- if (is_rename_source(diff, &opts, t, sigcache))
- ++num_srcs;
-
- if (is_rename_target(diff, &opts, t, sigcache))
- ++num_tgts;
-
- if ((tgt->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0)
- num_rewrites++;
- }
-
- /* if there are no candidate srcs or tgts, we're done */
- if (!num_srcs || !num_tgts)
- goto cleanup;
-
- src2tgt = git__calloc(num_deltas, sizeof(diff_find_match));
- GITERR_CHECK_ALLOC(src2tgt);
- tgt2src = git__calloc(num_deltas, sizeof(diff_find_match));
- GITERR_CHECK_ALLOC(tgt2src);
-
- if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) {
- tgt2src_copy = git__calloc(num_deltas, sizeof(diff_find_match));
- GITERR_CHECK_ALLOC(tgt2src_copy);
- }
-
- /*
- * Find best-fit matches for rename / copy candidates
- */
-
-find_best_matches:
- tried_tgts = num_bumped = 0;
-
- git_vector_foreach(&diff->deltas, t, tgt) {
- /* skip things that are not rename targets */
- if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
- continue;
-
- tried_srcs = 0;
-
- git_vector_foreach(&diff->deltas, s, src) {
- /* skip things that are not rename sources */
- if ((src->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
- continue;
-
- /* calculate similarity for this pair and find best match */
- if (s == t)
- result = -1; /* don't measure self-similarity here */
- else if ((error = similarity_measure(
- &result, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0)
- goto cleanup;
-
- if (result < 0)
- continue;
- similarity = (uint16_t)result;
-
- /* is this a better rename? */
- if (tgt2src[t].similarity < similarity &&
- src2tgt[s].similarity < similarity)
- {
- /* eject old mapping */
- if (src2tgt[s].similarity > 0) {
- tgt2src[src2tgt[s].idx].similarity = 0;
- num_bumped++;
- }
- if (tgt2src[t].similarity > 0) {
- src2tgt[tgt2src[t].idx].similarity = 0;
- num_bumped++;
- }
-
- /* write new mapping */
- tgt2src[t].idx = s;
- tgt2src[t].similarity = similarity;
- src2tgt[s].idx = t;
- src2tgt[s].similarity = similarity;
- }
-
- /* keep best absolute match for copies */
- if (tgt2src_copy != NULL &&
- tgt2src_copy[t].similarity < similarity)
- {
- tgt2src_copy[t].idx = s;
- tgt2src_copy[t].similarity = similarity;
- }
-
- if (++tried_srcs >= num_srcs)
- break;
-
- /* cap on maximum targets we'll examine (per "tgt" file) */
- if (tried_srcs > opts.rename_limit)
- break;
- }
-
- if (++tried_tgts >= num_tgts)
- break;
- }
-
- if (num_bumped > 0) /* try again if we bumped some items */
- goto find_best_matches;
-
- /*
- * Rewrite the diffs with renames / copies
- */
-
- git_vector_foreach(&diff->deltas, t, tgt) {
- /* skip things that are not rename targets */
- if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
- continue;
-
- /* check if this delta was the target of a similarity */
- if (tgt2src[t].similarity)
- best_match = &tgt2src[t];
- else if (tgt2src_copy && tgt2src_copy[t].similarity)
- best_match = &tgt2src_copy[t];
- else
- continue;
-
- s = best_match->idx;
- src = GIT_VECTOR_GET(&diff->deltas, s);
-
- /* possible scenarios:
- * 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME
- * 2. from DELETE to SPLIT/TYPECHANGE = RENAME + DELETE
- * 3. from SPLIT/TYPECHANGE to ADD/UNTRACK/IGNORE = ADD + RENAME
- * 4. from SPLIT/TYPECHANGE to SPLIT/TYPECHANGE = RENAME + SPLIT
- * 5. from OTHER to ADD/UNTRACK/IGNORE = OTHER + COPY
- */
-
- if (src->status == GIT_DELTA_DELETED) {
-
- if (delta_is_new_only(tgt)) {
-
- if (best_match->similarity < opts.rename_threshold)
- continue;
-
- delta_make_rename(tgt, src, best_match->similarity);
-
- src->flags |= GIT_DIFF_FLAG__TO_DELETE;
- num_rewrites++;
- } else {
- assert(delta_is_split(tgt));
-
- if (best_match->similarity < opts.rename_from_rewrite_threshold)
- continue;
-
- memcpy(&swap, &tgt->old_file, sizeof(swap));
-
- delta_make_rename(tgt, src, best_match->similarity);
- num_rewrites--;
-
- assert(src->status == GIT_DELTA_DELETED);
- memcpy(&src->old_file, &swap, sizeof(src->old_file));
- memset(&src->new_file, 0, sizeof(src->new_file));
- src->new_file.path = src->old_file.path;
- src->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
-
- num_updates++;
-
- if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
- /* what used to be at src t is now at src s */
- tgt2src[src2tgt[t].idx].idx = s;
- }
- }
- }
-
- else if (delta_is_split(src)) {
-
- if (delta_is_new_only(tgt)) {
-
- if (best_match->similarity < opts.rename_threshold)
- continue;
-
- delta_make_rename(tgt, src, best_match->similarity);
-
- src->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
- GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED;
- src->nfiles = 1;
- memset(&src->old_file, 0, sizeof(src->old_file));
- src->old_file.path = src->new_file.path;
- src->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
-
- src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
- num_rewrites--;
-
- num_updates++;
- } else {
- assert(delta_is_split(src));
-
- if (best_match->similarity < opts.rename_from_rewrite_threshold)
- continue;
-
- memcpy(&swap, &tgt->old_file, sizeof(swap));
-
- delta_make_rename(tgt, src, best_match->similarity);
- num_rewrites--;
- num_updates++;
-
- memcpy(&src->old_file, &swap, sizeof(src->old_file));
-
- /* if we've just swapped the new element into the correct
- * place, clear the SPLIT flag
- */
- if (tgt2src[s].idx == t &&
- tgt2src[s].similarity >
- opts.rename_from_rewrite_threshold) {
- src->status = GIT_DELTA_RENAMED;
- src->similarity = tgt2src[s].similarity;
- tgt2src[s].similarity = 0;
- src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
- num_rewrites--;
- }
- /* otherwise, if we just overwrote a source, update mapping */
- else if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
- /* what used to be at src t is now at src s */
- tgt2src[src2tgt[t].idx].idx = s;
- }
-
- num_updates++;
- }
- }
-
- else if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) {
- if (tgt2src_copy[t].similarity < opts.copy_threshold)
- continue;
-
- /* always use best possible source for copy */
- best_match = &tgt2src_copy[t];
- src = GIT_VECTOR_GET(&diff->deltas, best_match->idx);
-
- if (delta_is_split(tgt)) {
- error = insert_delete_side_of_split(diff, &diff->deltas, tgt);
- if (error < 0)
- goto cleanup;
- num_rewrites--;
- }
-
- if (!delta_is_split(tgt) && !delta_is_new_only(tgt))
- continue;
-
- tgt->status = GIT_DELTA_COPIED;
- tgt->similarity = best_match->similarity;
- tgt->nfiles = 2;
- memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file));
- tgt->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
-
- num_updates++;
- }
- }
-
- /*
- * Actually split and delete entries as needed
- */
-
- if (num_rewrites > 0 || num_updates > 0)
- error = apply_splits_and_deletes(
- diff, diff->deltas.length - num_rewrites,
- FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES) &&
- !FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY));
-
-cleanup:
- git__free(tgt2src);
- git__free(src2tgt);
- git__free(tgt2src_copy);
-
- if (sigcache) {
- for (t = 0; t < num_deltas * 2; ++t) {
- if (sigcache[t] != NULL)
- opts.metric->free_signature(sigcache[t], opts.metric->payload);
- }
- git__free(sigcache);
- }
-
- if (!given_opts || !given_opts->metric)
- git__free(opts.metric);
-
- return error;
-}
-
-#undef FLAG_SET
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_diff_tform_h__
-#define INCLUDE_diff_tform_h__
-
-extern int git_diff_find_similar__hashsig_for_file(
- void **out, const git_diff_file *f, const char *path, void *p);
-
-extern int git_diff_find_similar__hashsig_for_buf(
- void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
-
-extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
-
-extern int git_diff_find_similar__calc_similarity(
- int *score, void *siga, void *sigb, void *payload);
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "git2/errors.h"
-#include "common.h"
-#include "diff.h"
-#include "diff_driver.h"
-#include "diff_xdiff.h"
-#include "patch_generate.h"
-
-static int git_xdiff_scan_int(const char **str, int *value)
-{
- const char *scan = *str;
- int v = 0, digits = 0;
- /* find next digit */
- for (scan = *str; *scan && !git__isdigit(*scan); scan++);
- /* parse next number */
- for (; git__isdigit(*scan); scan++, digits++)
- v = (v * 10) + (*scan - '0');
- *str = scan;
- *value = v;
- return (digits > 0) ? 0 : -1;
-}
-
-static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header)
-{
- /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
- if (*header != '@')
- goto fail;
- if (git_xdiff_scan_int(&header, &hunk->old_start) < 0)
- goto fail;
- if (*header == ',') {
- if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0)
- goto fail;
- } else
- hunk->old_lines = 1;
- if (git_xdiff_scan_int(&header, &hunk->new_start) < 0)
- goto fail;
- if (*header == ',') {
- if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0)
- goto fail;
- } else
- hunk->new_lines = 1;
- if (hunk->old_start < 0 || hunk->new_start < 0)
- goto fail;
-
- return 0;
-
-fail:
- giterr_set(GITERR_INVALID, "Malformed hunk header from xdiff");
- return -1;
-}
-
-typedef struct {
- git_xdiff_output *xo;
- git_patch_generated *patch;
- git_diff_hunk hunk;
- int old_lineno, new_lineno;
- mmfile_t xd_old_data, xd_new_data;
-} git_xdiff_info;
-
-static int diff_update_lines(
- git_xdiff_info *info,
- git_diff_line *line,
- const char *content,
- size_t content_len)
-{
- const char *scan = content, *scan_end = content + content_len;
-
- for (line->num_lines = 0; scan < scan_end; ++scan)
- if (*scan == '\n')
- ++line->num_lines;
-
- line->content = content;
- line->content_len = content_len;
-
- /* expect " "/"-"/"+", then data */
- switch (line->origin) {
- case GIT_DIFF_LINE_ADDITION:
- case GIT_DIFF_LINE_DEL_EOFNL:
- line->old_lineno = -1;
- line->new_lineno = info->new_lineno;
- info->new_lineno += (int)line->num_lines;
- break;
- case GIT_DIFF_LINE_DELETION:
- case GIT_DIFF_LINE_ADD_EOFNL:
- line->old_lineno = info->old_lineno;
- line->new_lineno = -1;
- info->old_lineno += (int)line->num_lines;
- break;
- case GIT_DIFF_LINE_CONTEXT:
- case GIT_DIFF_LINE_CONTEXT_EOFNL:
- line->old_lineno = info->old_lineno;
- line->new_lineno = info->new_lineno;
- info->old_lineno += (int)line->num_lines;
- info->new_lineno += (int)line->num_lines;
- break;
- default:
- giterr_set(GITERR_INVALID, "Unknown diff line origin %02x",
- (unsigned int)line->origin);
- return -1;
- }
-
- return 0;
-}
-
-static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
-{
- git_xdiff_info *info = priv;
- git_patch_generated *patch = info->patch;
- const git_diff_delta *delta = patch->base.delta;
- git_patch_generated_output *output = &info->xo->output;
- git_diff_line line;
-
- if (len == 1) {
- output->error = git_xdiff_parse_hunk(&info->hunk, bufs[0].ptr);
- if (output->error < 0)
- return output->error;
-
- info->hunk.header_len = bufs[0].size;
- if (info->hunk.header_len >= sizeof(info->hunk.header))
- info->hunk.header_len = sizeof(info->hunk.header) - 1;
- memcpy(info->hunk.header, bufs[0].ptr, info->hunk.header_len);
- info->hunk.header[info->hunk.header_len] = '\0';
-
- if (output->hunk_cb != NULL &&
- (output->error = output->hunk_cb(
- delta, &info->hunk, output->payload)))
- return output->error;
-
- info->old_lineno = info->hunk.old_start;
- info->new_lineno = info->hunk.new_start;
- }
-
- if (len == 2 || len == 3) {
- /* expect " "/"-"/"+", then data */
- line.origin =
- (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
- (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
- GIT_DIFF_LINE_CONTEXT;
-
- if (line.origin == GIT_DIFF_LINE_ADDITION)
- line.content_offset = bufs[1].ptr - info->xd_new_data.ptr;
- else if (line.origin == GIT_DIFF_LINE_DELETION)
- line.content_offset = bufs[1].ptr - info->xd_old_data.ptr;
- else
- line.content_offset = -1;
-
- output->error = diff_update_lines(
- info, &line, bufs[1].ptr, bufs[1].size);
-
- if (!output->error && output->data_cb != NULL)
- output->error = output->data_cb(
- delta, &info->hunk, &line, output->payload);
- }
-
- if (len == 3 && !output->error) {
- /* If we have a '+' and a third buf, then we have added a line
- * without a newline and the old code had one, so DEL_EOFNL.
- * If we have a '-' and a third buf, then we have removed a line
- * with out a newline but added a blank line, so ADD_EOFNL.
- */
- line.origin =
- (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
- (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
- GIT_DIFF_LINE_CONTEXT_EOFNL;
-
- line.content_offset = -1;
-
- output->error = diff_update_lines(
- info, &line, bufs[2].ptr, bufs[2].size);
-
- if (!output->error && output->data_cb != NULL)
- output->error = output->data_cb(
- delta, &info->hunk, &line, output->payload);
- }
-
- return output->error;
-}
-
-static int git_xdiff(git_patch_generated_output *output, git_patch_generated *patch)
-{
- git_xdiff_output *xo = (git_xdiff_output *)output;
- git_xdiff_info info;
- git_diff_find_context_payload findctxt;
-
- memset(&info, 0, sizeof(info));
- info.patch = patch;
- info.xo = xo;
-
- xo->callback.priv = &info;
-
- git_diff_find_context_init(
- &xo->config.find_func, &findctxt, git_patch_generated_driver(patch));
- xo->config.find_func_priv = &findctxt;
-
- if (xo->config.find_func != NULL)
- xo->config.flags |= XDL_EMIT_FUNCNAMES;
- else
- xo->config.flags &= ~XDL_EMIT_FUNCNAMES;
-
- /* TODO: check ofile.opts_flags to see if driver-specific per-file
- * updates are needed to xo->params.flags
- */
-
- git_patch_generated_old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
- git_patch_generated_new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
-
- if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE ||
- info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) {
- giterr_set(GITERR_INVALID, "files too large for diff");
- return -1;
- }
-
- xdl_diff(&info.xd_old_data, &info.xd_new_data,
- &xo->params, &xo->config, &xo->callback);
-
- git_diff_find_context_clear(&findctxt);
-
- return xo->output.error;
-}
-
-void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
-{
- uint32_t flags = opts ? opts->flags : 0;
-
- xo->output.diff_cb = git_xdiff;
-
- xo->config.ctxlen = opts ? opts->context_lines : 3;
- xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0;
-
- if (flags & GIT_DIFF_IGNORE_WHITESPACE)
- xo->params.flags |= XDF_WHITESPACE_FLAGS;
- if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
- xo->params.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
- if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
- xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
-
- if (flags & GIT_DIFF_PATIENCE)
- xo->params.flags |= XDF_PATIENCE_DIFF;
- if (flags & GIT_DIFF_MINIMAL)
- xo->params.flags |= XDF_NEED_MINIMAL;
-
- xo->callback.outf = git_xdiff_cb;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_diff_xdiff_h__
-#define INCLUDE_diff_xdiff_h__
-
-#include "diff.h"
-#include "xdiff/xdiff.h"
-#include "patch_generate.h"
-
-/* xdiff cannot cope with large files. these files should not be passed to
- * xdiff. callers should treat these large files as binary.
- */
-#define GIT_XDIFF_MAX_SIZE (1024LL * 1024 * 1023)
-
-/* A git_xdiff_output is a git_patch_generate_output with extra fields
- * necessary to use libxdiff. Calling git_xdiff_init() will set the diff_cb
- * field of the output to use xdiff to generate the diffs.
- */
-typedef struct {
- git_patch_generated_output output;
-
- xdemitconf_t config;
- xpparam_t params;
- xdemitcb_t callback;
-} git_xdiff_output;
-
-void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "global.h"
-#include "posix.h"
-#include "buffer.h"
-
-/********************************************
- * New error handling
- ********************************************/
-
-static git_error g_git_oom_error = {
- "Out of memory",
- GITERR_NOMEMORY
-};
-
-static void set_error_from_buffer(int error_class)
-{
- git_error *error = &GIT_GLOBAL->error_t;
- git_buf *buf = &GIT_GLOBAL->error_buf;
-
- error->message = buf->ptr;
- error->klass = error_class;
-
- GIT_GLOBAL->last_error = error;
-}
-
-static void set_error(int error_class, char *string)
-{
- git_buf *buf = &GIT_GLOBAL->error_buf;
-
- git_buf_clear(buf);
- if (string) {
- git_buf_puts(buf, string);
- git__free(string);
- }
-
- set_error_from_buffer(error_class);
-}
-
-void giterr_set_oom(void)
-{
- GIT_GLOBAL->last_error = &g_git_oom_error;
-}
-
-void giterr_set(int error_class, const char *string, ...)
-{
- va_list arglist;
-#ifdef GIT_WIN32
- DWORD win32_error_code = (error_class == GITERR_OS) ? GetLastError() : 0;
-#endif
- int error_code = (error_class == GITERR_OS) ? errno : 0;
- git_buf *buf = &GIT_GLOBAL->error_buf;
-
- git_buf_clear(buf);
- if (string) {
- va_start(arglist, string);
- git_buf_vprintf(buf, string, arglist);
- va_end(arglist);
-
- if (error_class == GITERR_OS)
- git_buf_PUTS(buf, ": ");
- }
-
- if (error_class == GITERR_OS) {
-#ifdef GIT_WIN32
- char * win32_error = git_win32_get_error_message(win32_error_code);
- if (win32_error) {
- git_buf_puts(buf, win32_error);
- git__free(win32_error);
-
- SetLastError(0);
- }
- else
-#endif
- if (error_code)
- git_buf_puts(buf, strerror(error_code));
-
- if (error_code)
- errno = 0;
- }
-
- if (!git_buf_oom(buf))
- set_error_from_buffer(error_class);
-}
-
-void giterr_set_str(int error_class, const char *string)
-{
- git_buf *buf = &GIT_GLOBAL->error_buf;
-
- assert(string);
-
- if (!string)
- return;
-
- git_buf_clear(buf);
- git_buf_puts(buf, string);
- if (!git_buf_oom(buf))
- set_error_from_buffer(error_class);
-}
-
-int giterr_set_regex(const regex_t *regex, int error_code)
-{
- char error_buf[1024];
-
- assert(error_code);
-
- regerror(error_code, regex, error_buf, sizeof(error_buf));
- giterr_set_str(GITERR_REGEX, error_buf);
-
- if (error_code == REG_NOMATCH)
- return GIT_ENOTFOUND;
-
- return GIT_EINVALIDSPEC;
-}
-
-void giterr_clear(void)
-{
- if (GIT_GLOBAL->last_error != NULL) {
- set_error(0, NULL);
- GIT_GLOBAL->last_error = NULL;
- }
-
- errno = 0;
-#ifdef GIT_WIN32
- SetLastError(0);
-#endif
-}
-
-const git_error *giterr_last(void)
-{
- return GIT_GLOBAL->last_error;
-}
-
-int giterr_state_capture(git_error_state *state, int error_code)
-{
- git_error *error = GIT_GLOBAL->last_error;
- git_buf *error_buf = &GIT_GLOBAL->error_buf;
-
- memset(state, 0, sizeof(git_error_state));
-
- if (!error_code)
- return 0;
-
- state->error_code = error_code;
- state->oom = (error == &g_git_oom_error);
-
- if (error) {
- state->error_msg.klass = error->klass;
-
- if (state->oom)
- state->error_msg.message = g_git_oom_error.message;
- else
- state->error_msg.message = git_buf_detach(error_buf);
- }
-
- giterr_clear();
- return error_code;
-}
-
-int giterr_state_restore(git_error_state *state)
-{
- int ret = 0;
-
- giterr_clear();
-
- if (state && state->error_msg.message) {
- if (state->oom)
- giterr_set_oom();
- else
- set_error(state->error_msg.klass, state->error_msg.message);
-
- ret = state->error_code;
- memset(state, 0, sizeof(git_error_state));
- }
-
- return ret;
-}
-
-void giterr_state_free(git_error_state *state)
-{
- if (!state)
- return;
-
- if (!state->oom)
- git__free(state->error_msg.message);
-
- memset(state, 0, sizeof(git_error_state));
-}
-
-int giterr_system_last(void)
-{
-#ifdef GIT_WIN32
- return GetLastError();
-#else
- return errno;
-#endif
-}
-
-void giterr_system_set(int code)
-{
-#ifdef GIT_WIN32
- SetLastError(code);
-#else
- errno = code;
-#endif
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/oid.h"
-#include "git2/refs.h"
-#include "git2/revwalk.h"
-#include "git2/transport.h"
-
-#include "common.h"
-#include "remote.h"
-#include "refspec.h"
-#include "pack.h"
-#include "fetch.h"
-#include "netops.h"
-#include "repository.h"
-#include "refs.h"
-
-static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, git_refspec *tagspec, git_remote_autotag_option_t tagopt)
-{
- int match = 0;
-
- if (!git_reference_is_valid_name(head->name))
- return 0;
-
- if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
- /*
- * If tagopt is --tags, always request tags
- * in addition to the remote's refspecs
- */
- if (git_refspec_src_matches(tagspec, head->name))
- match = 1;
- }
-
- if (!match && git_remote__matching_refspec(remote, head->name))
- match = 1;
-
- if (!match)
- return 0;
-
- /* If we have the object, mark it so we don't ask for it */
- if (git_odb_exists(odb, &head->oid)) {
- head->local = 1;
- }
- else
- remote->need_pack = 1;
-
- return git_vector_insert(&remote->refs, head);
-}
-
-static int filter_wants(git_remote *remote, const git_fetch_options *opts)
-{
- git_remote_head **heads;
- git_refspec tagspec, head;
- int error = 0;
- git_odb *odb;
- size_t i, heads_len;
- git_remote_autotag_option_t tagopt = remote->download_tags;
-
- if (opts && opts->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED)
- tagopt = opts->download_tags;
-
- git_vector_clear(&remote->refs);
- if ((error = git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true)) < 0)
- return error;
-
- /*
- * The fetch refspec can be NULL, and what this means is that the
- * user didn't specify one. This is fine, as it means that we're
- * not interested in any particular branch but just the remote's
- * HEAD, which will be stored in FETCH_HEAD after the fetch.
- */
- if (remote->active_refspecs.length == 0) {
- if ((error = git_refspec__parse(&head, "HEAD", true)) < 0)
- goto cleanup;
-
- error = git_refspec__dwim_one(&remote->active_refspecs, &head, &remote->refs);
- git_refspec__free(&head);
-
- if (error < 0)
- goto cleanup;
- }
-
- if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
- goto cleanup;
-
- if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
- goto cleanup;
-
- for (i = 0; i < heads_len; i++) {
- if ((error = maybe_want(remote, heads[i], odb, &tagspec, tagopt)) < 0)
- break;
- }
-
-cleanup:
- git_refspec__free(&tagspec);
-
- return error;
-}
-
-/*
- * In this first version, we push all our refs in and start sending
- * them out. When we get an ACK we hide that commit and continue
- * traversing until we're done
- */
-int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts)
-{
- git_transport *t = remote->transport;
-
- remote->need_pack = 0;
-
- if (filter_wants(remote, opts) < 0) {
- giterr_set(GITERR_NET, "Failed to filter the reference list for wants");
- return -1;
- }
-
- /* Don't try to negotiate when we don't want anything */
- if (!remote->need_pack)
- return 0;
-
- /*
- * Now we have everything set up so we can start tell the
- * server what we want and what we have.
- */
- return t->negotiate_fetch(t,
- remote->repo,
- (const git_remote_head * const *)remote->refs.contents,
- remote->refs.length);
-}
-
-int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *callbacks)
-{
- git_transport *t = remote->transport;
- git_transfer_progress_cb progress = NULL;
- void *payload = NULL;
-
- if (!remote->need_pack)
- return 0;
-
- if (callbacks) {
- progress = callbacks->transfer_progress;
- payload = callbacks->payload;
- }
-
- return t->download_pack(t, remote->repo, &remote->stats, progress, payload);
-}
-
-int git_fetch_init_options(git_fetch_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_fetch_options, GIT_FETCH_OPTIONS_INIT);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_fetch_h__
-#define INCLUDE_fetch_h__
-
-#include "netops.h"
-
-int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts);
-
-int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *callbacks);
-
-int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/types.h"
-#include "git2/oid.h"
-
-#include "fetchhead.h"
-#include "common.h"
-#include "buffer.h"
-#include "fileops.h"
-#include "filebuf.h"
-#include "refs.h"
-#include "repository.h"
-
-int git_fetchhead_ref_cmp(const void *a, const void *b)
-{
- const git_fetchhead_ref *one = (const git_fetchhead_ref *)a;
- const git_fetchhead_ref *two = (const git_fetchhead_ref *)b;
-
- if (one->is_merge && !two->is_merge)
- return -1;
- if (two->is_merge && !one->is_merge)
- return 1;
-
- if (one->ref_name && two->ref_name)
- return strcmp(one->ref_name, two->ref_name);
- else if (one->ref_name)
- return -1;
- else if (two->ref_name)
- return 1;
-
- return 0;
-}
-
-int git_fetchhead_ref_create(
- git_fetchhead_ref **out,
- git_oid *oid,
- unsigned int is_merge,
- const char *ref_name,
- const char *remote_url)
-{
- git_fetchhead_ref *fetchhead_ref;
-
- assert(out && oid);
-
- *out = NULL;
-
- fetchhead_ref = git__malloc(sizeof(git_fetchhead_ref));
- GITERR_CHECK_ALLOC(fetchhead_ref);
-
- memset(fetchhead_ref, 0x0, sizeof(git_fetchhead_ref));
-
- git_oid_cpy(&fetchhead_ref->oid, oid);
- fetchhead_ref->is_merge = is_merge;
-
- if (ref_name)
- fetchhead_ref->ref_name = git__strdup(ref_name);
-
- if (remote_url)
- fetchhead_ref->remote_url = git__strdup(remote_url);
-
- *out = fetchhead_ref;
-
- return 0;
-}
-
-static int fetchhead_ref_write(
- git_filebuf *file,
- git_fetchhead_ref *fetchhead_ref)
-{
- char oid[GIT_OID_HEXSZ + 1];
- const char *type, *name;
- int head = 0;
-
- assert(file && fetchhead_ref);
-
- git_oid_fmt(oid, &fetchhead_ref->oid);
- oid[GIT_OID_HEXSZ] = '\0';
-
- if (git__prefixcmp(fetchhead_ref->ref_name, GIT_REFS_HEADS_DIR) == 0) {
- type = "branch ";
- name = fetchhead_ref->ref_name + strlen(GIT_REFS_HEADS_DIR);
- } else if(git__prefixcmp(fetchhead_ref->ref_name,
- GIT_REFS_TAGS_DIR) == 0) {
- type = "tag ";
- name = fetchhead_ref->ref_name + strlen(GIT_REFS_TAGS_DIR);
- } else if (!git__strcmp(fetchhead_ref->ref_name, GIT_HEAD_FILE)) {
- head = 1;
- } else {
- type = "";
- name = fetchhead_ref->ref_name;
- }
-
- if (head)
- return git_filebuf_printf(file, "%s\t\t%s\n", oid, fetchhead_ref->remote_url);
-
- return git_filebuf_printf(file, "%s\t%s\t%s'%s' of %s\n",
- oid,
- (fetchhead_ref->is_merge) ? "" : "not-for-merge",
- type,
- name,
- fetchhead_ref->remote_url);
-}
-
-int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf path = GIT_BUF_INIT;
- unsigned int i;
- git_fetchhead_ref *fetchhead_ref;
-
- assert(repo && fetchhead_refs);
-
- if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0)
- return -1;
-
- if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) {
- git_buf_free(&path);
- return -1;
- }
-
- git_buf_free(&path);
-
- git_vector_sort(fetchhead_refs);
-
- git_vector_foreach(fetchhead_refs, i, fetchhead_ref)
- fetchhead_ref_write(&file, fetchhead_ref);
-
- return git_filebuf_commit(&file);
-}
-
-static int fetchhead_ref_parse(
- git_oid *oid,
- unsigned int *is_merge,
- git_buf *ref_name,
- const char **remote_url,
- char *line,
- size_t line_num)
-{
- char *oid_str, *is_merge_str, *desc, *name = NULL;
- const char *type = NULL;
- int error = 0;
-
- *remote_url = NULL;
-
- if (!*line) {
- giterr_set(GITERR_FETCHHEAD,
- "Empty line in FETCH_HEAD line %"PRIuZ, line_num);
- return -1;
- }
-
- /* Compat with old git clients that wrote FETCH_HEAD like a loose ref. */
- if ((oid_str = git__strsep(&line, "\t")) == NULL) {
- oid_str = line;
- line += strlen(line);
-
- *is_merge = 1;
- }
-
- if (strlen(oid_str) != GIT_OID_HEXSZ) {
- giterr_set(GITERR_FETCHHEAD,
- "Invalid object ID in FETCH_HEAD line %"PRIuZ, line_num);
- return -1;
- }
-
- if (git_oid_fromstr(oid, oid_str) < 0) {
- const git_error *oid_err = giterr_last();
- const char *err_msg = oid_err ? oid_err->message : "Invalid object ID";
-
- giterr_set(GITERR_FETCHHEAD, "%s in FETCH_HEAD line %"PRIuZ,
- err_msg, line_num);
- return -1;
- }
-
- /* Parse new data from newer git clients */
- if (*line) {
- if ((is_merge_str = git__strsep(&line, "\t")) == NULL) {
- giterr_set(GITERR_FETCHHEAD,
- "Invalid description data in FETCH_HEAD line %"PRIuZ, line_num);
- return -1;
- }
-
- if (*is_merge_str == '\0')
- *is_merge = 1;
- else if (strcmp(is_merge_str, "not-for-merge") == 0)
- *is_merge = 0;
- else {
- giterr_set(GITERR_FETCHHEAD,
- "Invalid for-merge entry in FETCH_HEAD line %"PRIuZ, line_num);
- return -1;
- }
-
- if ((desc = line) == NULL) {
- giterr_set(GITERR_FETCHHEAD,
- "Invalid description in FETCH_HEAD line %"PRIuZ, line_num);
- return -1;
- }
-
- if (git__prefixcmp(desc, "branch '") == 0) {
- type = GIT_REFS_HEADS_DIR;
- name = desc + 8;
- } else if (git__prefixcmp(desc, "tag '") == 0) {
- type = GIT_REFS_TAGS_DIR;
- name = desc + 5;
- } else if (git__prefixcmp(desc, "'") == 0)
- name = desc + 1;
-
- if (name) {
- if ((desc = strstr(name, "' ")) == NULL ||
- git__prefixcmp(desc, "' of ") != 0) {
- giterr_set(GITERR_FETCHHEAD,
- "Invalid description in FETCH_HEAD line %"PRIuZ, line_num);
- return -1;
- }
-
- *desc = '\0';
- desc += 5;
- }
-
- *remote_url = desc;
- }
-
- git_buf_clear(ref_name);
-
- if (type)
- git_buf_join(ref_name, '/', type, name);
- else if(name)
- git_buf_puts(ref_name, name);
-
- return error;
-}
-
-int git_repository_fetchhead_foreach(git_repository *repo,
- git_repository_fetchhead_foreach_cb cb,
- void *payload)
-{
- git_buf path = GIT_BUF_INIT, file = GIT_BUF_INIT, name = GIT_BUF_INIT;
- const char *ref_name;
- git_oid oid;
- const char *remote_url;
- unsigned int is_merge = 0;
- char *buffer, *line;
- size_t line_num = 0;
- int error = 0;
-
- assert(repo && cb);
-
- if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0)
- return -1;
-
- if ((error = git_futils_readbuffer(&file, git_buf_cstr(&path))) < 0)
- goto done;
-
- buffer = file.ptr;
-
- while ((line = git__strsep(&buffer, "\n")) != NULL) {
- ++line_num;
-
- if ((error = fetchhead_ref_parse(
- &oid, &is_merge, &name, &remote_url, line, line_num)) < 0)
- goto done;
-
- if (git_buf_len(&name) > 0)
- ref_name = git_buf_cstr(&name);
- else
- ref_name = NULL;
-
- error = cb(ref_name, remote_url, &oid, is_merge, payload);
- if (error) {
- giterr_set_after_callback(error);
- goto done;
- }
- }
-
- if (*buffer) {
- giterr_set(GITERR_FETCHHEAD, "No EOL at line %"PRIuZ, line_num+1);
- error = -1;
- goto done;
- }
-
-done:
- git_buf_free(&file);
- git_buf_free(&path);
- git_buf_free(&name);
-
- return error;
-}
-
-void git_fetchhead_ref_free(git_fetchhead_ref *fetchhead_ref)
-{
- if (fetchhead_ref == NULL)
- return;
-
- git__free(fetchhead_ref->remote_url);
- git__free(fetchhead_ref->ref_name);
- git__free(fetchhead_ref);
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_fetchhead_h__
-#define INCLUDE_fetchhead_h__
-
-#include "vector.h"
-
-typedef struct git_fetchhead_ref {
- git_oid oid;
- unsigned int is_merge;
- char *ref_name;
- char *remote_url;
-} git_fetchhead_ref;
-
-int git_fetchhead_ref_create(
- git_fetchhead_ref **fetchhead_ref_out,
- git_oid *oid,
- unsigned int is_merge,
- const char *ref_name,
- const char *remote_url);
-
-int git_fetchhead_ref_cmp(const void *a, const void *b);
-
-int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs);
-
-void git_fetchhead_ref_free(git_fetchhead_ref *fetchhead_ref);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "filebuf.h"
-#include "fileops.h"
-
-static const size_t WRITE_BUFFER_SIZE = (4096 * 2);
-
-enum buferr_t {
- BUFERR_OK = 0,
- BUFERR_WRITE,
- BUFERR_ZLIB,
- BUFERR_MEM
-};
-
-#define ENSURE_BUF_OK(buf) if ((buf)->last_error != BUFERR_OK) { return -1; }
-
-static int verify_last_error(git_filebuf *file)
-{
- switch (file->last_error) {
- case BUFERR_WRITE:
- giterr_set(GITERR_OS, "Failed to write out file");
- return -1;
-
- case BUFERR_MEM:
- giterr_set_oom();
- return -1;
-
- case BUFERR_ZLIB:
- giterr_set(GITERR_ZLIB,
- "Buffer error when writing out ZLib data");
- return -1;
-
- default:
- return 0;
- }
-}
-
-static int lock_file(git_filebuf *file, int flags, mode_t mode)
-{
- if (git_path_exists(file->path_lock) == true) {
- if (flags & GIT_FILEBUF_FORCE)
- p_unlink(file->path_lock);
- else {
- giterr_clear(); /* actual OS error code just confuses */
- giterr_set(GITERR_OS,
- "Failed to lock file '%s' for writing", file->path_lock);
- return GIT_ELOCKED;
- }
- }
-
- /* create path to the file buffer is required */
- if (flags & GIT_FILEBUF_FORCE) {
- /* XXX: Should dirmode here be configurable? Or is 0777 always fine? */
- file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, mode);
- } else {
- file->fd = git_futils_creat_locked(file->path_lock, mode);
- }
-
- if (file->fd < 0)
- return file->fd;
-
- file->fd_is_open = true;
-
- if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) {
- git_file source;
- char buffer[FILEIO_BUFSIZE];
- ssize_t read_bytes;
- int error = 0;
-
- source = p_open(file->path_original, O_RDONLY);
- if (source < 0) {
- giterr_set(GITERR_OS,
- "Failed to open file '%s' for reading",
- file->path_original);
- return -1;
- }
-
- while ((read_bytes = p_read(source, buffer, sizeof(buffer))) > 0) {
- if ((error = p_write(file->fd, buffer, read_bytes)) < 0)
- break;
- if (file->compute_digest)
- git_hash_update(&file->digest, buffer, read_bytes);
- }
-
- p_close(source);
-
- if (read_bytes < 0) {
- giterr_set(GITERR_OS, "Failed to read file '%s'", file->path_original);
- return -1;
- } else if (error < 0) {
- giterr_set(GITERR_OS, "Failed to write file '%s'", file->path_lock);
- return -1;
- }
- }
-
- return 0;
-}
-
-void git_filebuf_cleanup(git_filebuf *file)
-{
- if (file->fd_is_open && file->fd >= 0)
- p_close(file->fd);
-
- if (file->created_lock && !file->did_rename && file->path_lock && git_path_exists(file->path_lock))
- p_unlink(file->path_lock);
-
- if (file->compute_digest) {
- git_hash_ctx_cleanup(&file->digest);
- file->compute_digest = 0;
- }
-
- if (file->buffer)
- git__free(file->buffer);
-
- /* use the presence of z_buf to decide if we need to deflateEnd */
- if (file->z_buf) {
- git__free(file->z_buf);
- deflateEnd(&file->zs);
- }
-
- if (file->path_original)
- git__free(file->path_original);
- if (file->path_lock)
- git__free(file->path_lock);
-
- memset(file, 0x0, sizeof(git_filebuf));
- file->fd = -1;
-}
-
-GIT_INLINE(int) flush_buffer(git_filebuf *file)
-{
- int result = file->write(file, file->buffer, file->buf_pos);
- file->buf_pos = 0;
- return result;
-}
-
-int git_filebuf_flush(git_filebuf *file)
-{
- return flush_buffer(file);
-}
-
-static int write_normal(git_filebuf *file, void *source, size_t len)
-{
- if (len > 0) {
- if (p_write(file->fd, (void *)source, len) < 0) {
- file->last_error = BUFERR_WRITE;
- return -1;
- }
-
- if (file->compute_digest)
- git_hash_update(&file->digest, source, len);
- }
-
- return 0;
-}
-
-static int write_deflate(git_filebuf *file, void *source, size_t len)
-{
- z_stream *zs = &file->zs;
-
- if (len > 0 || file->flush_mode == Z_FINISH) {
- zs->next_in = source;
- zs->avail_in = (uInt)len;
-
- do {
- size_t have;
-
- zs->next_out = file->z_buf;
- zs->avail_out = (uInt)file->buf_size;
-
- if (deflate(zs, file->flush_mode) == Z_STREAM_ERROR) {
- file->last_error = BUFERR_ZLIB;
- return -1;
- }
-
- have = file->buf_size - (size_t)zs->avail_out;
-
- if (p_write(file->fd, file->z_buf, have) < 0) {
- file->last_error = BUFERR_WRITE;
- return -1;
- }
-
- } while (zs->avail_out == 0);
-
- assert(zs->avail_in == 0);
-
- if (file->compute_digest)
- git_hash_update(&file->digest, source, len);
- }
-
- return 0;
-}
-
-#define MAX_SYMLINK_DEPTH 5
-
-static int resolve_symlink(git_buf *out, const char *path)
-{
- int i, error, root;
- ssize_t ret;
- struct stat st;
- git_buf curpath = GIT_BUF_INIT, target = GIT_BUF_INIT;
-
- if ((error = git_buf_grow(&target, GIT_PATH_MAX + 1)) < 0 ||
- (error = git_buf_puts(&curpath, path)) < 0)
- return error;
-
- for (i = 0; i < MAX_SYMLINK_DEPTH; i++) {
- error = p_lstat(curpath.ptr, &st);
- if (error < 0 && errno == ENOENT) {
- error = git_buf_puts(out, curpath.ptr);
- goto cleanup;
- }
-
- if (error < 0) {
- giterr_set(GITERR_OS, "failed to stat '%s'", curpath.ptr);
- error = -1;
- goto cleanup;
- }
-
- if (!S_ISLNK(st.st_mode)) {
- error = git_buf_puts(out, curpath.ptr);
- goto cleanup;
- }
-
- ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX);
- if (ret < 0) {
- giterr_set(GITERR_OS, "failed to read symlink '%s'", curpath.ptr);
- error = -1;
- goto cleanup;
- }
-
- if (ret == GIT_PATH_MAX) {
- giterr_set(GITERR_INVALID, "symlink target too long");
- error = -1;
- goto cleanup;
- }
-
- /* readlink(2) won't NUL-terminate for us */
- target.ptr[ret] = '\0';
- target.size = ret;
-
- root = git_path_root(target.ptr);
- if (root >= 0) {
- if ((error = git_buf_puts(&curpath, target.ptr)) < 0)
- goto cleanup;
- } else {
- git_buf dir = GIT_BUF_INIT;
-
- if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0)
- goto cleanup;
-
- git_buf_swap(&curpath, &dir);
- git_buf_free(&dir);
-
- if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0)
- goto cleanup;
- }
- }
-
- giterr_set(GITERR_INVALID, "maximum symlink depth reached");
- error = -1;
-
-cleanup:
- git_buf_free(&curpath);
- git_buf_free(&target);
- return error;
-}
-
-int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
-{
- return git_filebuf_open_withsize(file, path, flags, mode, WRITE_BUFFER_SIZE);
-}
-
-int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size)
-{
- int compression, error = -1;
- size_t path_len, alloc_len;
-
- /* opening an already open buffer is a programming error;
- * assert that this never happens instead of returning
- * an error code */
- assert(file && path && file->buffer == NULL);
-
- memset(file, 0x0, sizeof(git_filebuf));
-
- if (flags & GIT_FILEBUF_DO_NOT_BUFFER)
- file->do_not_buffer = true;
-
- file->buf_size = size;
- file->buf_pos = 0;
- file->fd = -1;
- file->last_error = BUFERR_OK;
-
- /* Allocate the main cache buffer */
- if (!file->do_not_buffer) {
- file->buffer = git__malloc(file->buf_size);
- GITERR_CHECK_ALLOC(file->buffer);
- }
-
- /* If we are hashing on-write, allocate a new hash context */
- if (flags & GIT_FILEBUF_HASH_CONTENTS) {
- file->compute_digest = 1;
-
- if (git_hash_ctx_init(&file->digest) < 0)
- goto cleanup;
- }
-
- compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT;
-
- /* If we are deflating on-write, */
- if (compression != 0) {
- /* Initialize the ZLib stream */
- if (deflateInit(&file->zs, compression) != Z_OK) {
- giterr_set(GITERR_ZLIB, "Failed to initialize zlib");
- goto cleanup;
- }
-
- /* Allocate the Zlib cache buffer */
- file->z_buf = git__malloc(file->buf_size);
- GITERR_CHECK_ALLOC(file->z_buf);
-
- /* Never flush */
- file->flush_mode = Z_NO_FLUSH;
- file->write = &write_deflate;
- } else {
- file->write = &write_normal;
- }
-
- /* If we are writing to a temp file */
- if (flags & GIT_FILEBUF_TEMPORARY) {
- git_buf tmp_path = GIT_BUF_INIT;
-
- /* Open the file as temporary for locking */
- file->fd = git_futils_mktmp(&tmp_path, path, mode);
-
- if (file->fd < 0) {
- git_buf_free(&tmp_path);
- goto cleanup;
- }
- file->fd_is_open = true;
- file->created_lock = true;
-
- /* No original path */
- file->path_original = NULL;
- file->path_lock = git_buf_detach(&tmp_path);
- GITERR_CHECK_ALLOC(file->path_lock);
- } else {
- git_buf resolved_path = GIT_BUF_INIT;
-
- if ((error = resolve_symlink(&resolved_path, path)) < 0)
- goto cleanup;
-
- /* Save the original path of the file */
- path_len = resolved_path.size;
- file->path_original = git_buf_detach(&resolved_path);
-
- /* create the locking path by appending ".lock" to the original */
- GITERR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH);
- file->path_lock = git__malloc(alloc_len);
- GITERR_CHECK_ALLOC(file->path_lock);
-
- memcpy(file->path_lock, file->path_original, path_len);
- memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
-
- if (git_path_isdir(file->path_original)) {
- giterr_set(GITERR_FILESYSTEM, "path '%s' is a directory", file->path_original);
- error = GIT_EDIRECTORY;
- goto cleanup;
- }
-
- /* open the file for locking */
- if ((error = lock_file(file, flags, mode)) < 0)
- goto cleanup;
-
- file->created_lock = true;
- }
-
- return 0;
-
-cleanup:
- git_filebuf_cleanup(file);
- return error;
-}
-
-int git_filebuf_hash(git_oid *oid, git_filebuf *file)
-{
- assert(oid && file && file->compute_digest);
-
- flush_buffer(file);
-
- if (verify_last_error(file) < 0)
- return -1;
-
- git_hash_final(oid, &file->digest);
- git_hash_ctx_cleanup(&file->digest);
- file->compute_digest = 0;
-
- return 0;
-}
-
-int git_filebuf_commit_at(git_filebuf *file, const char *path)
-{
- git__free(file->path_original);
- file->path_original = git__strdup(path);
- GITERR_CHECK_ALLOC(file->path_original);
-
- return git_filebuf_commit(file);
-}
-
-int git_filebuf_commit(git_filebuf *file)
-{
- /* temporary files cannot be committed */
- assert(file && file->path_original);
-
- file->flush_mode = Z_FINISH;
- flush_buffer(file);
-
- if (verify_last_error(file) < 0)
- goto on_error;
-
- file->fd_is_open = false;
-
- if (p_close(file->fd) < 0) {
- giterr_set(GITERR_OS, "Failed to close file at '%s'", file->path_lock);
- goto on_error;
- }
-
- file->fd = -1;
-
- if (p_rename(file->path_lock, file->path_original) < 0) {
- giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original);
- goto on_error;
- }
-
- file->did_rename = true;
-
- git_filebuf_cleanup(file);
- return 0;
-
-on_error:
- git_filebuf_cleanup(file);
- return -1;
-}
-
-GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)
-{
- memcpy(file->buffer + file->buf_pos, buf, len);
- file->buf_pos += len;
-}
-
-int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)
-{
- const unsigned char *buf = buff;
-
- ENSURE_BUF_OK(file);
-
- if (file->do_not_buffer)
- return file->write(file, (void *)buff, len);
-
- for (;;) {
- size_t space_left = file->buf_size - file->buf_pos;
-
- /* cache if it's small */
- if (space_left > len) {
- add_to_cache(file, buf, len);
- return 0;
- }
-
- add_to_cache(file, buf, space_left);
- if (flush_buffer(file) < 0)
- return -1;
-
- len -= space_left;
- buf += space_left;
- }
-}
-
-int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len)
-{
- size_t space_left = file->buf_size - file->buf_pos;
-
- *buffer = NULL;
-
- ENSURE_BUF_OK(file);
-
- if (len > file->buf_size) {
- file->last_error = BUFERR_MEM;
- return -1;
- }
-
- if (space_left <= len) {
- if (flush_buffer(file) < 0)
- return -1;
- }
-
- *buffer = (file->buffer + file->buf_pos);
- file->buf_pos += len;
-
- return 0;
-}
-
-int git_filebuf_printf(git_filebuf *file, const char *format, ...)
-{
- va_list arglist;
- size_t space_left, len, alloclen;
- int written, res;
- char *tmp_buffer;
-
- ENSURE_BUF_OK(file);
-
- space_left = file->buf_size - file->buf_pos;
-
- do {
- va_start(arglist, format);
- written = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);
- va_end(arglist);
-
- if (written < 0) {
- file->last_error = BUFERR_MEM;
- return -1;
- }
-
- len = written;
- if (len + 1 <= space_left) {
- file->buf_pos += len;
- return 0;
- }
-
- if (flush_buffer(file) < 0)
- return -1;
-
- space_left = file->buf_size - file->buf_pos;
-
- } while (len + 1 <= space_left);
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclen, len, 1) ||
- !(tmp_buffer = git__malloc(alloclen))) {
- file->last_error = BUFERR_MEM;
- return -1;
- }
-
- va_start(arglist, format);
- written = p_vsnprintf(tmp_buffer, len + 1, format, arglist);
- va_end(arglist);
-
- if (written < 0) {
- git__free(tmp_buffer);
- file->last_error = BUFERR_MEM;
- return -1;
- }
-
- res = git_filebuf_write(file, tmp_buffer, len);
- git__free(tmp_buffer);
-
- return res;
-}
-
-int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file)
-{
- int res;
- struct stat st;
-
- if (file->fd_is_open)
- res = p_fstat(file->fd, &st);
- else
- res = p_stat(file->path_original, &st);
-
- if (res < 0) {
- giterr_set(GITERR_OS, "Could not get stat info for '%s'",
- file->path_original);
- return res;
- }
-
- if (mtime)
- *mtime = st.st_mtime;
- if (size)
- *size = (size_t)st.st_size;
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_filebuf_h__
-#define INCLUDE_filebuf_h__
-
-#include "fileops.h"
-#include "hash.h"
-#include <zlib.h>
-
-#ifdef GIT_THREADS
-# define GIT_FILEBUF_THREADS
-#endif
-
-#define GIT_FILEBUF_HASH_CONTENTS (1 << 0)
-#define GIT_FILEBUF_APPEND (1 << 2)
-#define GIT_FILEBUF_FORCE (1 << 3)
-#define GIT_FILEBUF_TEMPORARY (1 << 4)
-#define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5)
-#define GIT_FILEBUF_DEFLATE_SHIFT (6)
-
-#define GIT_FILELOCK_EXTENSION ".lock\0"
-#define GIT_FILELOCK_EXTLENGTH 6
-
-typedef struct git_filebuf git_filebuf;
-struct git_filebuf {
- char *path_original;
- char *path_lock;
-
- int (*write)(git_filebuf *file, void *source, size_t len);
-
- bool compute_digest;
- git_hash_ctx digest;
-
- unsigned char *buffer;
- unsigned char *z_buf;
-
- z_stream zs;
- int flush_mode;
-
- size_t buf_size, buf_pos;
- git_file fd;
- bool fd_is_open;
- bool created_lock;
- bool did_rename;
- bool do_not_buffer;
- int last_error;
-};
-
-#define GIT_FILEBUF_INIT {0}
-
-/*
- * The git_filebuf object lifecycle is:
- * - Allocate git_filebuf, preferably using GIT_FILEBUF_INIT.
- *
- * - Call git_filebuf_open() to initialize the filebuf for use.
- *
- * - Make as many calls to git_filebuf_write(), git_filebuf_printf(),
- * git_filebuf_reserve() as you like. The error codes for these
- * functions don't need to be checked. They are stored internally
- * by the file buffer.
- *
- * - While you are writing, you may call git_filebuf_hash() to get
- * the hash of all you have written so far. This function will
- * fail if any of the previous writes to the buffer failed.
- *
- * - To close the git_filebuf, you may call git_filebuf_commit() or
- * git_filebuf_commit_at() to save the file, or
- * git_filebuf_cleanup() to abandon the file. All of these will
- * free the git_filebuf object. Likewise, all of these will fail
- * if any of the previous writes to the buffer failed, and set
- * an error code accordingly.
- */
-int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len);
-int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
-int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
-
-int git_filebuf_open(git_filebuf *lock, const char *path, int flags, mode_t mode);
-int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size);
-int git_filebuf_commit(git_filebuf *lock);
-int git_filebuf_commit_at(git_filebuf *lock, const char *path);
-void git_filebuf_cleanup(git_filebuf *lock);
-int git_filebuf_hash(git_oid *oid, git_filebuf *file);
-int git_filebuf_flush(git_filebuf *file);
-int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "fileops.h"
-#include "global.h"
-#include "strmap.h"
-#include <ctype.h>
-#if GIT_WIN32
-#include "win32/findfile.h"
-#endif
-
-GIT__USE_STRMAP
-
-int git_futils_mkpath2file(const char *file_path, const mode_t mode)
-{
- return git_futils_mkdir(
- file_path, mode,
- GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
-}
-
-int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode)
-{
- int fd;
- mode_t mask;
-
- p_umask(mask = p_umask(0));
-
- git_buf_sets(path_out, filename);
- git_buf_puts(path_out, "_git2_XXXXXX");
-
- if (git_buf_oom(path_out))
- return -1;
-
- if ((fd = p_mkstemp(path_out->ptr)) < 0) {
- giterr_set(GITERR_OS,
- "Failed to create temporary file '%s'", path_out->ptr);
- return -1;
- }
-
- if (p_chmod(path_out->ptr, (mode & ~mask))) {
- giterr_set(GITERR_OS,
- "Failed to set permissions on file '%s'", path_out->ptr);
- return -1;
- }
-
- return fd;
-}
-
-int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)
-{
- int fd;
-
- if (git_futils_mkpath2file(path, dirmode) < 0)
- return -1;
-
- fd = p_creat(path, mode);
- if (fd < 0) {
- giterr_set(GITERR_OS, "Failed to create file '%s'", path);
- return -1;
- }
-
- return fd;
-}
-
-int git_futils_creat_locked(const char *path, const mode_t mode)
-{
- int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC |
- O_EXCL | O_BINARY | O_CLOEXEC, mode);
-
- if (fd < 0) {
- int error = errno;
- giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
- switch (error) {
- case EEXIST:
- return GIT_ELOCKED;
- case ENOENT:
- return GIT_ENOTFOUND;
- default:
- return -1;
- }
- }
-
- return fd;
-}
-
-int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode)
-{
- if (git_futils_mkpath2file(path, dirmode) < 0)
- return -1;
-
- return git_futils_creat_locked(path, mode);
-}
-
-int git_futils_open_ro(const char *path)
-{
- int fd = p_open(path, O_RDONLY);
- if (fd < 0)
- return git_path_set_error(errno, path, "open");
- return fd;
-}
-
-git_off_t git_futils_filesize(git_file fd)
-{
- struct stat sb;
-
- if (p_fstat(fd, &sb)) {
- giterr_set(GITERR_OS, "Failed to stat file descriptor");
- return -1;
- }
-
- return sb.st_size;
-}
-
-mode_t git_futils_canonical_mode(mode_t raw_mode)
-{
- if (S_ISREG(raw_mode))
- return S_IFREG | GIT_PERMS_CANONICAL(raw_mode);
- else if (S_ISLNK(raw_mode))
- return S_IFLNK;
- else if (S_ISGITLINK(raw_mode))
- return S_IFGITLINK;
- else if (S_ISDIR(raw_mode))
- return S_IFDIR;
- else
- return 0;
-}
-
-int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
-{
- ssize_t read_size = 0;
- size_t alloc_len;
-
- git_buf_clear(buf);
-
- if (!git__is_ssizet(len)) {
- giterr_set(GITERR_INVALID, "Read too large.");
- return -1;
- }
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
- if (git_buf_grow(buf, alloc_len) < 0)
- return -1;
-
- /* p_read loops internally to read len bytes */
- read_size = p_read(fd, buf->ptr, len);
-
- if (read_size != (ssize_t)len) {
- giterr_set(GITERR_OS, "Failed to read descriptor");
- git_buf_free(buf);
- return -1;
- }
-
- buf->ptr[read_size] = '\0';
- buf->size = read_size;
-
- return 0;
-}
-
-int git_futils_readbuffer_updated(
- git_buf *out, const char *path, git_oid *checksum, int *updated)
-{
- int error;
- git_file fd;
- struct stat st;
- git_buf buf = GIT_BUF_INIT;
- git_oid checksum_new;
-
- assert(out && path && *path);
-
- if (updated != NULL)
- *updated = 0;
-
- if (p_stat(path, &st) < 0)
- return git_path_set_error(errno, path, "stat");
-
-
- if (S_ISDIR(st.st_mode)) {
- giterr_set(GITERR_INVALID, "requested file is a directory");
- return GIT_ENOTFOUND;
- }
-
- if (!git__is_sizet(st.st_size+1)) {
- giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
- return -1;
- }
-
- if ((fd = git_futils_open_ro(path)) < 0)
- return fd;
-
- if (git_futils_readbuffer_fd(&buf, fd, (size_t)st.st_size) < 0) {
- p_close(fd);
- return -1;
- }
-
- p_close(fd);
-
- if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) {
- git_buf_free(&buf);
- return error;
- }
-
- /*
- * If we were given a checksum, we only want to use it if it's different
- */
- if (checksum && !git_oid__cmp(checksum, &checksum_new)) {
- git_buf_free(&buf);
- if (updated)
- *updated = 0;
-
- return 0;
- }
-
- /*
- * If we're here, the file did change, or the user didn't have an old version
- */
- if (checksum)
- git_oid_cpy(checksum, &checksum_new);
-
- if (updated != NULL)
- *updated = 1;
-
- git_buf_swap(out, &buf);
- git_buf_free(&buf);
-
- return 0;
-}
-
-int git_futils_readbuffer(git_buf *buf, const char *path)
-{
- return git_futils_readbuffer_updated(buf, path, NULL, NULL);
-}
-
-int git_futils_writebuffer(
- const git_buf *buf, const char *path, int flags, mode_t mode)
-{
- int fd, error = 0;
-
- if (flags <= 0)
- flags = O_CREAT | O_TRUNC | O_WRONLY;
- if (!mode)
- mode = GIT_FILEMODE_BLOB;
-
- if ((fd = p_open(path, flags, mode)) < 0) {
- giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
- return fd;
- }
-
- if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) {
- giterr_set(GITERR_OS, "Could not write to '%s'", path);
- (void)p_close(fd);
- return error;
- }
-
- if ((error = p_close(fd)) < 0)
- giterr_set(GITERR_OS, "Error while closing '%s'", path);
-
- return error;
-}
-
-int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
-{
- if (git_futils_mkpath2file(to, dirmode) < 0)
- return -1;
-
- if (p_rename(from, to) < 0) {
- giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to);
- return -1;
- }
-
- return 0;
-}
-
-int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
-{
- return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
-}
-
-int git_futils_mmap_ro_file(git_map *out, const char *path)
-{
- git_file fd = git_futils_open_ro(path);
- git_off_t len;
- int result;
-
- if (fd < 0)
- return fd;
-
- len = git_futils_filesize(fd);
- if (!git__is_sizet(len)) {
- giterr_set(GITERR_OS, "File `%s` too large to mmap", path);
- return -1;
- }
-
- result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
- p_close(fd);
- return result;
-}
-
-void git_futils_mmap_free(git_map *out)
-{
- p_munmap(out);
-}
-
-GIT_INLINE(int) mkdir_validate_dir(
- const char *path,
- struct stat *st,
- mode_t mode,
- uint32_t flags,
- struct git_futils_mkdir_options *opts)
-{
- /* with exclusive create, existing dir is an error */
- if ((flags & GIT_MKDIR_EXCL) != 0) {
- giterr_set(GITERR_FILESYSTEM,
- "Failed to make directory '%s': directory exists", path);
- return GIT_EEXISTS;
- }
-
- if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) ||
- (S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) {
- if (p_unlink(path) < 0) {
- giterr_set(GITERR_OS, "Failed to remove %s '%s'",
- S_ISLNK(st->st_mode) ? "symlink" : "file", path);
- return GIT_EEXISTS;
- }
-
- opts->perfdata.mkdir_calls++;
-
- if (p_mkdir(path, mode) < 0) {
- giterr_set(GITERR_OS, "Failed to make directory '%s'", path);
- return GIT_EEXISTS;
- }
- }
-
- else if (S_ISLNK(st->st_mode)) {
- /* Re-stat the target, make sure it's a directory */
- opts->perfdata.stat_calls++;
-
- if (p_stat(path, st) < 0) {
- giterr_set(GITERR_OS, "Failed to make directory '%s'", path);
- return GIT_EEXISTS;
- }
- }
-
- else if (!S_ISDIR(st->st_mode)) {
- giterr_set(GITERR_FILESYSTEM,
- "Failed to make directory '%s': directory exists", path);
- return GIT_EEXISTS;
- }
-
- return 0;
-}
-
-GIT_INLINE(int) mkdir_validate_mode(
- const char *path,
- struct stat *st,
- bool terminal_path,
- mode_t mode,
- uint32_t flags,
- struct git_futils_mkdir_options *opts)
-{
- if (((terminal_path && (flags & GIT_MKDIR_CHMOD) != 0) ||
- (flags & GIT_MKDIR_CHMOD_PATH) != 0) && st->st_mode != mode) {
-
- opts->perfdata.chmod_calls++;
-
- if (p_chmod(path, mode) < 0) {
- giterr_set(GITERR_OS, "failed to set permissions on '%s'", path);
- return -1;
- }
- }
-
- return 0;
-}
-
-GIT_INLINE(int) mkdir_canonicalize(
- git_buf *path,
- uint32_t flags)
-{
- ssize_t root_len;
-
- if (path->size == 0) {
- giterr_set(GITERR_OS, "attempt to create empty path");
- return -1;
- }
-
- /* Trim trailing slashes (except the root) */
- if ((root_len = git_path_root(path->ptr)) < 0)
- root_len = 0;
- else
- root_len++;
-
- while (path->size > (size_t)root_len && path->ptr[path->size - 1] == '/')
- path->ptr[--path->size] = '\0';
-
- /* if we are not supposed to made the last element, truncate it */
- if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
- git_path_dirname_r(path, path->ptr);
- flags |= GIT_MKDIR_SKIP_LAST;
- }
- if ((flags & GIT_MKDIR_SKIP_LAST) != 0) {
- git_path_dirname_r(path, path->ptr);
- }
-
- /* We were either given the root path (or trimmed it to
- * the root), we don't have anything to do.
- */
- if (path->size <= (size_t)root_len)
- git_buf_clear(path);
-
- return 0;
-}
-
-int git_futils_mkdir(
- const char *path,
- mode_t mode,
- uint32_t flags)
-{
- git_buf make_path = GIT_BUF_INIT, parent_path = GIT_BUF_INIT;
- const char *relative;
- struct git_futils_mkdir_options opts = { 0 };
- struct stat st;
- size_t depth = 0;
- int len = 0, root_len, error;
-
- if ((error = git_buf_puts(&make_path, path)) < 0 ||
- (error = mkdir_canonicalize(&make_path, flags)) < 0 ||
- (error = git_buf_puts(&parent_path, make_path.ptr)) < 0 ||
- make_path.size == 0)
- goto done;
-
- root_len = git_path_root(make_path.ptr);
-
- /* find the first parent directory that exists. this will be used
- * as the base to dirname_relative.
- */
- for (relative = make_path.ptr; parent_path.size; ) {
- error = p_lstat(parent_path.ptr, &st);
-
- if (error == 0) {
- break;
- } else if (errno != ENOENT) {
- giterr_set(GITERR_OS, "failed to stat '%s'", parent_path.ptr);
- goto done;
- }
-
- depth++;
-
- /* examine the parent of the current path */
- if ((len = git_path_dirname_r(&parent_path, parent_path.ptr)) < 0) {
- error = len;
- goto done;
- }
-
- assert(len);
-
- /* we've walked all the given path's parents and it's either relative
- * or rooted. either way, give up and make the entire path.
- */
- if ((len == 1 && parent_path.ptr[0] == '.') || len == root_len+1) {
- relative = make_path.ptr;
- break;
- }
-
- relative = make_path.ptr + len + 1;
-
- /* not recursive? just make this directory relative to its parent. */
- if ((flags & GIT_MKDIR_PATH) == 0)
- break;
- }
-
- /* we found an item at the location we're trying to create,
- * validate it.
- */
- if (depth == 0) {
- error = mkdir_validate_dir(make_path.ptr, &st, mode, flags, &opts);
-
- if (!error)
- error = mkdir_validate_mode(
- make_path.ptr, &st, true, mode, flags, &opts);
-
- goto done;
- }
-
- /* we already took `SKIP_LAST` and `SKIP_LAST2` into account when
- * canonicalizing `make_path`.
- */
- flags &= ~(GIT_MKDIR_SKIP_LAST2 | GIT_MKDIR_SKIP_LAST);
-
- error = git_futils_mkdir_relative(relative,
- parent_path.size ? parent_path.ptr : NULL, mode, flags, &opts);
-
-done:
- git_buf_free(&make_path);
- git_buf_free(&parent_path);
- return error;
-}
-
-int git_futils_mkdir_r(const char *path, const mode_t mode)
-{
- return git_futils_mkdir(path, mode, GIT_MKDIR_PATH);
-}
-
-int git_futils_mkdir_relative(
- const char *relative_path,
- const char *base,
- mode_t mode,
- uint32_t flags,
- struct git_futils_mkdir_options *opts)
-{
- git_buf make_path = GIT_BUF_INIT;
- ssize_t root = 0, min_root_len;
- char lastch = '/', *tail;
- struct stat st;
- struct git_futils_mkdir_options empty_opts = {0};
- int error;
-
- if (!opts)
- opts = &empty_opts;
-
- /* build path and find "root" where we should start calling mkdir */
- if (git_path_join_unrooted(&make_path, relative_path, base, &root) < 0)
- return -1;
-
- if ((error = mkdir_canonicalize(&make_path, flags)) < 0 ||
- make_path.size == 0)
- goto done;
-
- /* if we are not supposed to make the whole path, reset root */
- if ((flags & GIT_MKDIR_PATH) == 0)
- root = git_buf_rfind(&make_path, '/');
-
- /* advance root past drive name or network mount prefix */
- min_root_len = git_path_root(make_path.ptr);
- if (root < min_root_len)
- root = min_root_len;
- while (root >= 0 && make_path.ptr[root] == '/')
- ++root;
-
- /* clip root to make_path length */
- if (root > (ssize_t)make_path.size)
- root = (ssize_t)make_path.size; /* i.e. NUL byte of string */
- if (root < 0)
- root = 0;
-
- /* walk down tail of path making each directory */
- for (tail = &make_path.ptr[root]; *tail; *tail = lastch) {
- bool mkdir_attempted = false;
-
- /* advance tail to include next path component */
- while (*tail == '/')
- tail++;
- while (*tail && *tail != '/')
- tail++;
-
- /* truncate path at next component */
- lastch = *tail;
- *tail = '\0';
- st.st_mode = 0;
-
- if (opts->dir_map && git_strmap_exists(opts->dir_map, make_path.ptr))
- continue;
-
- /* See what's going on with this path component */
- opts->perfdata.stat_calls++;
-
-retry_lstat:
- if (p_lstat(make_path.ptr, &st) < 0) {
- if (mkdir_attempted || errno != ENOENT) {
- giterr_set(GITERR_OS, "Cannot access component in path '%s'", make_path.ptr);
- error = -1;
- goto done;
- }
-
- giterr_clear();
- opts->perfdata.mkdir_calls++;
- mkdir_attempted = true;
- if (p_mkdir(make_path.ptr, mode) < 0) {
- if (errno == EEXIST)
- goto retry_lstat;
- giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
- error = -1;
- goto done;
- }
- } else {
- if ((error = mkdir_validate_dir(
- make_path.ptr, &st, mode, flags, opts)) < 0)
- goto done;
- }
-
- /* chmod if requested and necessary */
- if ((error = mkdir_validate_mode(
- make_path.ptr, &st, (lastch == '\0'), mode, flags, opts)) < 0)
- goto done;
-
- if (opts->dir_map && opts->pool) {
- char *cache_path;
- size_t alloc_size;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_size, make_path.size, 1);
- if (!git__is_uint32(alloc_size))
- return -1;
- cache_path = git_pool_malloc(opts->pool, (uint32_t)alloc_size);
- GITERR_CHECK_ALLOC(cache_path);
-
- memcpy(cache_path, make_path.ptr, make_path.size + 1);
-
- git_strmap_insert(opts->dir_map, cache_path, cache_path, error);
- if (error < 0)
- goto done;
- }
- }
-
- error = 0;
-
- /* check that full path really is a directory if requested & needed */
- if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
- lastch != '\0') {
- opts->perfdata.stat_calls++;
-
- if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
- giterr_set(GITERR_OS, "Path is not a directory '%s'",
- make_path.ptr);
- error = GIT_ENOTFOUND;
- }
- }
-
-done:
- git_buf_free(&make_path);
- return error;
-}
-
-typedef struct {
- const char *base;
- size_t baselen;
- uint32_t flags;
- int depth;
-} futils__rmdir_data;
-
-#define FUTILS_MAX_DEPTH 100
-
-static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
-{
- if (filemsg)
- giterr_set(GITERR_OS, "Could not remove directory. File '%s' %s",
- path, filemsg);
- else
- giterr_set(GITERR_OS, "Could not remove directory '%s'", path);
-
- return -1;
-}
-
-static int futils__rm_first_parent(git_buf *path, const char *ceiling)
-{
- int error = GIT_ENOTFOUND;
- struct stat st;
-
- while (error == GIT_ENOTFOUND) {
- git_buf_rtruncate_at_char(path, '/');
-
- if (!path->size || git__prefixcmp(path->ptr, ceiling) != 0)
- error = 0;
- else if (p_lstat_posixly(path->ptr, &st) == 0) {
- if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
- error = p_unlink(path->ptr);
- else if (!S_ISDIR(st.st_mode))
- error = -1; /* fail to remove non-regular file */
- } else if (errno != ENOTDIR)
- error = -1;
- }
-
- if (error)
- futils__error_cannot_rmdir(path->ptr, "cannot remove parent");
-
- return error;
-}
-
-static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
-{
- int error = 0;
- futils__rmdir_data *data = opaque;
- struct stat st;
-
- if (data->depth > FUTILS_MAX_DEPTH)
- error = futils__error_cannot_rmdir(
- path->ptr, "directory nesting too deep");
-
- else if ((error = p_lstat_posixly(path->ptr, &st)) < 0) {
- if (errno == ENOENT)
- error = 0;
- else if (errno == ENOTDIR) {
- /* asked to remove a/b/c/d/e and a/b is a normal file */
- if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0)
- error = futils__rm_first_parent(path, data->base);
- else
- futils__error_cannot_rmdir(
- path->ptr, "parent is not directory");
- }
- else
- error = git_path_set_error(errno, path->ptr, "rmdir");
- }
-
- else if (S_ISDIR(st.st_mode)) {
- data->depth++;
-
- error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data);
-
- data->depth--;
-
- if (error < 0)
- return error;
-
- if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0)
- return error;
-
- if ((error = p_rmdir(path->ptr)) < 0) {
- if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
- (errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
- error = 0;
- else
- error = git_path_set_error(errno, path->ptr, "rmdir");
- }
- }
-
- else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
- if (p_unlink(path->ptr) < 0)
- error = git_path_set_error(errno, path->ptr, "remove");
- }
-
- else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0)
- error = futils__error_cannot_rmdir(path->ptr, "still present");
-
- return error;
-}
-
-static int futils__rmdir_empty_parent(void *opaque, const char *path)
-{
- futils__rmdir_data *data = opaque;
- int error = 0;
-
- if (strlen(path) <= data->baselen)
- error = GIT_ITEROVER;
-
- else if (p_rmdir(path) < 0) {
- int en = errno;
-
- if (en == ENOENT || en == ENOTDIR) {
- /* do nothing */
- } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) {
- error = GIT_ITEROVER;
- } else {
- error = git_path_set_error(errno, path, "rmdir");
- }
- }
-
- return error;
-}
-
-int git_futils_rmdir_r(
- const char *path, const char *base, uint32_t flags)
-{
- int error;
- git_buf fullpath = GIT_BUF_INIT;
- futils__rmdir_data data;
-
- /* build path and find "root" where we should start calling mkdir */
- if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
- return -1;
-
- memset(&data, 0, sizeof(data));
- data.base = base ? base : "";
- data.baselen = base ? strlen(base) : 0;
- data.flags = flags;
-
- error = futils__rmdir_recurs_foreach(&data, &fullpath);
-
- /* remove now-empty parents if requested */
- if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0)
- error = git_path_walk_up(
- &fullpath, base, futils__rmdir_empty_parent, &data);
-
- if (error == GIT_ITEROVER) {
- giterr_clear();
- error = 0;
- }
-
- git_buf_free(&fullpath);
-
- return error;
-}
-
-int git_futils_fake_symlink(const char *old, const char *new)
-{
- int retcode = GIT_ERROR;
- int fd = git_futils_creat_withpath(new, 0755, 0644);
- if (fd >= 0) {
- retcode = p_write(fd, old, strlen(old));
- p_close(fd);
- }
- return retcode;
-}
-
-static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done)
-{
- int error = 0;
- char buffer[FILEIO_BUFSIZE];
- ssize_t len = 0;
-
- while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0)
- /* p_write() does not have the same semantics as write(). It loops
- * internally and will return 0 when it has completed writing.
- */
- error = p_write(ofd, buffer, len);
-
- if (len < 0) {
- giterr_set(GITERR_OS, "Read error while copying file");
- error = (int)len;
- }
-
- if (error < 0)
- giterr_set(GITERR_OS, "write error while copying file");
-
- if (close_fd_when_done) {
- p_close(ifd);
- p_close(ofd);
- }
-
- return error;
-}
-
-int git_futils_cp(const char *from, const char *to, mode_t filemode)
-{
- int ifd, ofd;
-
- if ((ifd = git_futils_open_ro(from)) < 0)
- return ifd;
-
- if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
- p_close(ifd);
- return git_path_set_error(errno, to, "open for writing");
- }
-
- return cp_by_fd(ifd, ofd, true);
-}
-
-int git_futils_touch(const char *path, time_t *when)
-{
- struct p_timeval times[2];
- int ret;
-
- times[0].tv_sec = times[1].tv_sec = when ? *when : time(NULL);
- times[0].tv_usec = times[1].tv_usec = 0;
-
- ret = p_utimes(path, times);
-
- return (ret < 0) ? git_path_set_error(errno, path, "touch") : 0;
-}
-
-static int cp_link(const char *from, const char *to, size_t link_size)
-{
- int error = 0;
- ssize_t read_len;
- char *link_data;
- size_t alloc_size;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_size, link_size, 1);
- link_data = git__malloc(alloc_size);
- GITERR_CHECK_ALLOC(link_data);
-
- read_len = p_readlink(from, link_data, link_size);
- if (read_len != (ssize_t)link_size) {
- giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", from);
- error = -1;
- }
- else {
- link_data[read_len] = '\0';
-
- if (p_symlink(link_data, to) < 0) {
- giterr_set(GITERR_OS, "Could not symlink '%s' as '%s'",
- link_data, to);
- error = -1;
- }
- }
-
- git__free(link_data);
- return error;
-}
-
-typedef struct {
- const char *to_root;
- git_buf to;
- ssize_t from_prefix;
- uint32_t flags;
- uint32_t mkdir_flags;
- mode_t dirmode;
-} cp_r_info;
-
-#define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
-
-static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
-{
- int error = 0;
-
- /* create root directory the first time we need to create a directory */
- if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) {
- error = git_futils_mkdir(
- info->to_root, info->dirmode,
- (info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0);
-
- info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT;
- }
-
- /* create directory with root as base to prevent excess chmods */
- if (!error)
- error = git_futils_mkdir_relative(
- from->ptr + info->from_prefix, info->to_root,
- info->dirmode, info->mkdir_flags, NULL);
-
- return error;
-}
-
-static int _cp_r_callback(void *ref, git_buf *from)
-{
- int error = 0;
- cp_r_info *info = ref;
- struct stat from_st, to_st;
- bool exists = false;
-
- if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 &&
- from->ptr[git_path_basename_offset(from)] == '.')
- return 0;
-
- if ((error = git_buf_joinpath(
- &info->to, info->to_root, from->ptr + info->from_prefix)) < 0)
- return error;
-
- if (!(error = git_path_lstat(info->to.ptr, &to_st)))
- exists = true;
- else if (error != GIT_ENOTFOUND)
- return error;
- else {
- giterr_clear();
- error = 0;
- }
-
- if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
- return error;
-
- if (S_ISDIR(from_st.st_mode)) {
- mode_t oldmode = info->dirmode;
-
- /* if we are not chmod'ing, then overwrite dirmode */
- if ((info->flags & GIT_CPDIR_CHMOD_DIRS) == 0)
- info->dirmode = from_st.st_mode;
-
- /* make directory now if CREATE_EMPTY_DIRS is requested and needed */
- if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0)
- error = _cp_r_mkdir(info, from);
-
- /* recurse onto target directory */
- if (!error && (!exists || S_ISDIR(to_st.st_mode)))
- error = git_path_direach(from, 0, _cp_r_callback, info);
-
- if (oldmode != 0)
- info->dirmode = oldmode;
-
- return error;
- }
-
- if (exists) {
- if ((info->flags & GIT_CPDIR_OVERWRITE) == 0)
- return 0;
-
- if (p_unlink(info->to.ptr) < 0) {
- giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
- info->to.ptr);
- return GIT_EEXISTS;
- }
- }
-
- /* Done if this isn't a regular file or a symlink */
- if (!S_ISREG(from_st.st_mode) &&
- (!S_ISLNK(from_st.st_mode) ||
- (info->flags & GIT_CPDIR_COPY_SYMLINKS) == 0))
- return 0;
-
- /* Make container directory on demand if needed */
- if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
- (error = _cp_r_mkdir(info, from)) < 0)
- return error;
-
- /* make symlink or regular file */
- if (info->flags & GIT_CPDIR_LINK_FILES) {
- if ((error = p_link(from->ptr, info->to.ptr)) < 0)
- giterr_set(GITERR_OS, "failed to link '%s'", from->ptr);
- } else if (S_ISLNK(from_st.st_mode)) {
- error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
- } else {
- mode_t usemode = from_st.st_mode;
-
- if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
- usemode = GIT_PERMS_FOR_WRITE(usemode);
-
- error = git_futils_cp(from->ptr, info->to.ptr, usemode);
- }
-
- return error;
-}
-
-int git_futils_cp_r(
- const char *from,
- const char *to,
- uint32_t flags,
- mode_t dirmode)
-{
- int error;
- git_buf path = GIT_BUF_INIT;
- cp_r_info info;
-
- if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */
- return -1;
-
- memset(&info, 0, sizeof(info));
- info.to_root = to;
- info.flags = flags;
- info.dirmode = dirmode;
- info.from_prefix = path.size;
- git_buf_init(&info.to, 0);
-
- /* precalculate mkdir flags */
- if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) {
- /* if not creating empty dirs, then use mkdir to create the path on
- * demand right before files are copied.
- */
- info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST;
- if ((flags & GIT_CPDIR_CHMOD_DIRS) != 0)
- info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
- } else {
- /* otherwise, we will do simple mkdir as directories are encountered */
- info.mkdir_flags =
- ((flags & GIT_CPDIR_CHMOD_DIRS) != 0) ? GIT_MKDIR_CHMOD : 0;
- }
-
- error = _cp_r_callback(&info, &path);
-
- git_buf_free(&path);
- git_buf_free(&info.to);
-
- return error;
-}
-
-int git_futils_filestamp_check(
- git_futils_filestamp *stamp, const char *path)
-{
- struct stat st;
-
- /* if the stamp is NULL, then always reload */
- if (stamp == NULL)
- return 1;
-
- if (p_stat(path, &st) < 0)
- return GIT_ENOTFOUND;
-
- if (stamp->mtime.tv_sec == st.st_mtime &&
-#if defined(GIT_USE_NSEC)
- stamp->mtime.tv_nsec == st.st_mtime_nsec &&
-#endif
- stamp->size == (git_off_t)st.st_size &&
- stamp->ino == (unsigned int)st.st_ino)
- return 0;
-
- stamp->mtime.tv_sec = st.st_mtime;
-#if defined(GIT_USE_NSEC)
- stamp->mtime.tv_nsec = st.st_mtime_nsec;
-#endif
- stamp->size = (git_off_t)st.st_size;
- stamp->ino = (unsigned int)st.st_ino;
-
- return 1;
-}
-
-void git_futils_filestamp_set(
- git_futils_filestamp *target, const git_futils_filestamp *source)
-{
- assert(target);
-
- if (source)
- memcpy(target, source, sizeof(*target));
- else
- memset(target, 0, sizeof(*target));
-}
-
-
-void git_futils_filestamp_set_from_stat(
- git_futils_filestamp *stamp, struct stat *st)
-{
- if (st) {
- stamp->mtime.tv_sec = st->st_mtime;
-#if defined(GIT_USE_NSEC)
- stamp->mtime.tv_nsec = st->st_mtime_nsec;
-#else
- stamp->mtime.tv_nsec = 0;
-#endif
- stamp->size = (git_off_t)st->st_size;
- stamp->ino = (unsigned int)st->st_ino;
- } else {
- memset(stamp, 0, sizeof(*stamp));
- }
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_fileops_h__
-#define INCLUDE_fileops_h__
-
-#include "common.h"
-#include "map.h"
-#include "posix.h"
-#include "path.h"
-#include "pool.h"
-#include "strmap.h"
-#include "oid.h"
-
-/**
- * Filebuffer methods
- *
- * Read whole files into an in-memory buffer for processing
- */
-extern int git_futils_readbuffer(git_buf *obj, const char *path);
-extern int git_futils_readbuffer_updated(
- git_buf *obj, const char *path, git_oid *checksum, int *updated);
-extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len);
-
-extern int git_futils_writebuffer(
- const git_buf *buf, const char *path, int open_flags, mode_t mode);
-
-/**
- * File utils
- *
- * These are custom filesystem-related helper methods. They are
- * rather high level, and wrap the underlying POSIX methods
- *
- * All these methods return 0 on success,
- * or an error code on failure and an error message is set.
- */
-
-/**
- * Create and open a file, while also
- * creating all the folders in its path
- */
-extern int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode);
-
-/**
- * Create and open a process-locked file
- */
-extern int git_futils_creat_locked(const char *path, const mode_t mode);
-
-/**
- * Create and open a process-locked file, while
- * also creating all the folders in its path
- */
-extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode);
-
-/**
- * Create a path recursively.
- */
-extern int git_futils_mkdir_r(const char *path, const mode_t mode);
-
-/**
- * Flags to pass to `git_futils_mkdir`.
- *
- * * GIT_MKDIR_EXCL is "exclusive" - i.e. generate an error if dir exists.
- * * GIT_MKDIR_PATH says to make all components in the path.
- * * GIT_MKDIR_CHMOD says to chmod the final directory entry after creation
- * * GIT_MKDIR_CHMOD_PATH says to chmod each directory component in the path
- * * GIT_MKDIR_SKIP_LAST says to leave off the last element of the path
- * * GIT_MKDIR_SKIP_LAST2 says to leave off the last 2 elements of the path
- * * GIT_MKDIR_VERIFY_DIR says confirm final item is a dir, not just EEXIST
- * * GIT_MKDIR_REMOVE_FILES says to remove files and recreate dirs
- * * GIT_MKDIR_REMOVE_SYMLINKS says to remove symlinks and recreate dirs
- *
- * Note that the chmod options will be executed even if the directory already
- * exists, unless GIT_MKDIR_EXCL is given.
- */
-typedef enum {
- GIT_MKDIR_EXCL = 1,
- GIT_MKDIR_PATH = 2,
- GIT_MKDIR_CHMOD = 4,
- GIT_MKDIR_CHMOD_PATH = 8,
- GIT_MKDIR_SKIP_LAST = 16,
- GIT_MKDIR_SKIP_LAST2 = 32,
- GIT_MKDIR_VERIFY_DIR = 64,
- GIT_MKDIR_REMOVE_FILES = 128,
- GIT_MKDIR_REMOVE_SYMLINKS = 256,
-} git_futils_mkdir_flags;
-
-struct git_futils_mkdir_perfdata
-{
- size_t stat_calls;
- size_t mkdir_calls;
- size_t chmod_calls;
-};
-
-struct git_futils_mkdir_options
-{
- git_strmap *dir_map;
- git_pool *pool;
- struct git_futils_mkdir_perfdata perfdata;
-};
-
-/**
- * Create a directory or entire path.
- *
- * This makes a directory (and the entire path leading up to it if requested),
- * and optionally chmods the directory immediately after (or each part of the
- * path if requested).
- *
- * @param path The path to create, relative to base.
- * @param base Root for relative path. These directories will never be made.
- * @param mode The mode to use for created directories.
- * @param flags Combination of the mkdir flags above.
- * @param opts Extended options, or null.
- * @return 0 on success, else error code
- */
-extern int git_futils_mkdir_relative(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts);
-
-/**
- * Create a directory or entire path. Similar to `git_futils_mkdir_relative`
- * without performance data.
- */
-extern int git_futils_mkdir(const char *path, mode_t mode, uint32_t flags);
-
-/**
- * Create all the folders required to contain
- * the full path of a file
- */
-extern int git_futils_mkpath2file(const char *path, const mode_t mode);
-
-/**
- * Flags to pass to `git_futils_rmdir_r`.
- *
- * * GIT_RMDIR_EMPTY_HIERARCHY - the default; remove hierarchy of empty
- * dirs and generate error if any files are found.
- * * GIT_RMDIR_REMOVE_FILES - attempt to remove files in the hierarchy.
- * * GIT_RMDIR_SKIP_NONEMPTY - skip non-empty directories with no error.
- * * GIT_RMDIR_EMPTY_PARENTS - remove containing directories up to base
- * if removing this item leaves them empty
- * * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
- * * GIT_RMDIR_SKIP_ROOT - don't remove root directory itself
- */
-typedef enum {
- GIT_RMDIR_EMPTY_HIERARCHY = 0,
- GIT_RMDIR_REMOVE_FILES = (1 << 0),
- GIT_RMDIR_SKIP_NONEMPTY = (1 << 1),
- GIT_RMDIR_EMPTY_PARENTS = (1 << 2),
- GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
- GIT_RMDIR_SKIP_ROOT = (1 << 4),
-} git_futils_rmdir_flags;
-
-/**
- * Remove path and any files and directories beneath it.
- *
- * @param path Path to the top level directory to process.
- * @param base Root for relative path.
- * @param flags Combination of git_futils_rmdir_flags values
- * @return 0 on success; -1 on error.
- */
-extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
-
-/**
- * Create and open a temporary file with a `_git2_` suffix.
- * Writes the filename into path_out.
- * @return On success, an open file descriptor, else an error code < 0.
- */
-extern int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode);
-
-/**
- * Move a file on the filesystem, create the
- * destination path if it doesn't exist
- */
-extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode);
-
-/**
- * Copy a file
- *
- * The filemode will be used for the newly created file.
- */
-extern int git_futils_cp(
- const char *from,
- const char *to,
- mode_t filemode);
-
-/**
- * Set the files atime and mtime to the given time, or the current time
- * if `ts` is NULL.
- */
-extern int git_futils_touch(const char *path, time_t *when);
-
-/**
- * Flags that can be passed to `git_futils_cp_r`.
- *
- * - GIT_CPDIR_CREATE_EMPTY_DIRS: create directories even if there are no
- * files under them (otherwise directories will only be created lazily
- * when a file inside them is copied).
- * - GIT_CPDIR_COPY_SYMLINKS: copy symlinks, otherwise they are ignored.
- * - GIT_CPDIR_COPY_DOTFILES: copy files with leading '.', otherwise ignored.
- * - GIT_CPDIR_OVERWRITE: overwrite pre-existing files with source content,
- * otherwise they are silently skipped.
- * - GIT_CPDIR_CHMOD_DIRS: explicitly chmod directories to `dirmode`
- * - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the
- * source file to the target; with this flag, always use 0666 (or 0777 if
- * source has exec bits set) for target.
- * - GIT_CPDIR_LINK_FILES will try to use hardlinks for the files
- */
-typedef enum {
- GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
- GIT_CPDIR_COPY_SYMLINKS = (1u << 1),
- GIT_CPDIR_COPY_DOTFILES = (1u << 2),
- GIT_CPDIR_OVERWRITE = (1u << 3),
- GIT_CPDIR_CHMOD_DIRS = (1u << 4),
- GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5),
- GIT_CPDIR_LINK_FILES = (1u << 6),
-} git_futils_cpdir_flags;
-
-/**
- * Copy a directory tree.
- *
- * This copies directories and files from one root to another. You can
- * pass a combinationof GIT_CPDIR flags as defined above.
- *
- * If you pass the CHMOD flag, then the dirmode will be applied to all
- * directories that are created during the copy, overiding the natural
- * permissions. If you do not pass the CHMOD flag, then the dirmode
- * will actually be copied from the source files and the `dirmode` arg
- * will be ignored.
- */
-extern int git_futils_cp_r(
- const char *from,
- const char *to,
- uint32_t flags,
- mode_t dirmode);
-
-/**
- * Open a file readonly and set error if needed.
- */
-extern int git_futils_open_ro(const char *path);
-
-/**
- * Get the filesize in bytes of a file
- */
-extern git_off_t git_futils_filesize(git_file fd);
-
-#define GIT_PERMS_IS_EXEC(MODE) (((MODE) & 0111) != 0)
-#define GIT_PERMS_CANONICAL(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0755 : 0644)
-#define GIT_PERMS_FOR_WRITE(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0777 : 0666)
-
-#define GIT_MODE_PERMS_MASK 0777
-#define GIT_MODE_TYPE_MASK 0170000
-#define GIT_MODE_TYPE(MODE) ((MODE) & GIT_MODE_TYPE_MASK)
-#define GIT_MODE_ISBLOB(MODE) (GIT_MODE_TYPE(MODE) == GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
-
-/**
- * Convert a mode_t from the OS to a legal git mode_t value.
- */
-extern mode_t git_futils_canonical_mode(mode_t raw_mode);
-
-
-/**
- * Read-only map all or part of a file into memory.
- * When possible this function should favor a virtual memory
- * style mapping over some form of malloc()+read(), as the
- * data access will be random and is not likely to touch the
- * majority of the region requested.
- *
- * @param out buffer to populate with the mapping information.
- * @param fd open descriptor to configure the mapping from.
- * @param begin first byte to map, this should be page aligned.
- * @param len number of bytes to map.
- * @return
- * - 0 on success;
- * - -1 on error.
- */
-extern int git_futils_mmap_ro(
- git_map *out,
- git_file fd,
- git_off_t begin,
- size_t len);
-
-/**
- * Read-only map an entire file.
- *
- * @param out buffer to populate with the mapping information.
- * @param path path to file to be opened.
- * @return
- * - 0 on success;
- * - GIT_ENOTFOUND if not found;
- * - -1 on an unspecified OS related error.
- */
-extern int git_futils_mmap_ro_file(
- git_map *out,
- const char *path);
-
-/**
- * Release the memory associated with a previous memory mapping.
- * @param map the mapping description previously configured.
- */
-extern void git_futils_mmap_free(git_map *map);
-
-/**
- * Create a "fake" symlink (text file containing the target path).
- *
- * @param new symlink file to be created
- * @param old original symlink target
- * @return 0 on success, -1 on error
- */
-extern int git_futils_fake_symlink(const char *new, const char *old);
-
-/**
- * A file stamp represents a snapshot of information about a file that can
- * be used to test if the file changes. This portable implementation is
- * based on stat data about that file, but it is possible that OS specific
- * versions could be implemented in the future.
- */
-typedef struct {
- struct timespec mtime;
- git_off_t size;
- unsigned int ino;
-} git_futils_filestamp;
-
-/**
- * Compare stat information for file with reference info.
- *
- * This function updates the file stamp to current data for the given path
- * and returns 0 if the file is up-to-date relative to the prior setting,
- * 1 if the file has been changed, or GIT_ENOTFOUND if the file doesn't
- * exist. This will not call giterr_set, so you must set the error if you
- * plan to return an error.
- *
- * @param stamp File stamp to be checked
- * @param path Path to stat and check if changed
- * @return 0 if up-to-date, 1 if out-of-date, GIT_ENOTFOUND if cannot stat
- */
-extern int git_futils_filestamp_check(
- git_futils_filestamp *stamp, const char *path);
-
-/**
- * Set or reset file stamp data
- *
- * This writes the target file stamp. If the source is NULL, this will set
- * the target stamp to values that will definitely be out of date. If the
- * source is not NULL, this copies the source values to the target.
- *
- * @param tgt File stamp to write to
- * @param src File stamp to copy from or NULL to clear the target
- */
-extern void git_futils_filestamp_set(
- git_futils_filestamp *tgt, const git_futils_filestamp *src);
-
-/**
- * Set file stamp data from stat structure
- */
-extern void git_futils_filestamp_set_from_stat(
- git_futils_filestamp *stamp, struct stat *st);
-
-#endif /* INCLUDE_fileops_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "fileops.h"
-#include "hash.h"
-#include "filter.h"
-#include "repository.h"
-#include "global.h"
-#include "git2/sys/filter.h"
-#include "git2/config.h"
-#include "blob.h"
-#include "attr_file.h"
-#include "array.h"
-
-struct git_filter_source {
- git_repository *repo;
- const char *path;
- git_oid oid; /* zero if unknown (which is likely) */
- uint16_t filemode; /* zero if unknown */
- git_filter_mode_t mode;
- uint32_t flags;
-};
-
-typedef struct {
- const char *filter_name;
- git_filter *filter;
- void *payload;
-} git_filter_entry;
-
-struct git_filter_list {
- git_array_t(git_filter_entry) filters;
- git_filter_source source;
- git_buf *temp_buf;
- char path[GIT_FLEX_ARRAY];
-};
-
-typedef struct {
- char *filter_name;
- git_filter *filter;
- int priority;
- int initialized;
- size_t nattrs, nmatches;
- char *attrdata;
- const char *attrs[GIT_FLEX_ARRAY];
-} git_filter_def;
-
-static int filter_def_priority_cmp(const void *a, const void *b)
-{
- int pa = ((const git_filter_def *)a)->priority;
- int pb = ((const git_filter_def *)b)->priority;
- return (pa < pb) ? -1 : (pa > pb) ? 1 : 0;
-}
-
-struct git_filter_registry {
- git_rwlock lock;
- git_vector filters;
-};
-
-static struct git_filter_registry filter_registry;
-
-static void git_filter_global_shutdown(void);
-
-
-static int filter_def_scan_attrs(
- git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str)
-{
- const char *start, *scan = attr_str;
- int has_eq;
-
- *nattr = *nmatch = 0;
-
- if (!scan)
- return 0;
-
- while (*scan) {
- while (git__isspace(*scan)) scan++;
-
- for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) {
- if (*scan == '=')
- has_eq = 1;
- }
-
- if (scan > start) {
- (*nattr)++;
- if (has_eq || *start == '-' || *start == '+' || *start == '!')
- (*nmatch)++;
-
- if (has_eq)
- git_buf_putc(attrs, '=');
- git_buf_put(attrs, start, scan - start);
- git_buf_putc(attrs, '\0');
- }
- }
-
- return 0;
-}
-
-static void filter_def_set_attrs(git_filter_def *fdef)
-{
- char *scan = fdef->attrdata;
- size_t i;
-
- for (i = 0; i < fdef->nattrs; ++i) {
- const char *name, *value;
-
- switch (*scan) {
- case '=':
- name = scan + 1;
- for (scan++; *scan != '='; scan++) /* find '=' */;
- *scan++ = '\0';
- value = scan;
- break;
- case '-':
- name = scan + 1; value = git_attr__false; break;
- case '+':
- name = scan + 1; value = git_attr__true; break;
- case '!':
- name = scan + 1; value = git_attr__unset; break;
- default:
- name = scan; value = NULL; break;
- }
-
- fdef->attrs[i] = name;
- fdef->attrs[i + fdef->nattrs] = value;
-
- scan += strlen(scan) + 1;
- }
-}
-
-static int filter_def_name_key_check(const void *key, const void *fdef)
-{
- const char *name =
- fdef ? ((const git_filter_def *)fdef)->filter_name : NULL;
- return name ? git__strcmp(key, name) : -1;
-}
-
-static int filter_def_filter_key_check(const void *key, const void *fdef)
-{
- const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL;
- return (key == filter) ? 0 : -1;
-}
-
-/* Note: callers must lock the registry before calling this function */
-static int filter_registry_insert(
- const char *name, git_filter *filter, int priority)
-{
- git_filter_def *fdef;
- size_t nattr = 0, nmatch = 0, alloc_len;
- git_buf attrs = GIT_BUF_INIT;
-
- if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0)
- return -1;
-
- GITERR_CHECK_ALLOC_MULTIPLY(&alloc_len, nattr, 2);
- GITERR_CHECK_ALLOC_MULTIPLY(&alloc_len, alloc_len, sizeof(char *));
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, sizeof(git_filter_def));
-
- fdef = git__calloc(1, alloc_len);
- GITERR_CHECK_ALLOC(fdef);
-
- fdef->filter_name = git__strdup(name);
- GITERR_CHECK_ALLOC(fdef->filter_name);
-
- fdef->filter = filter;
- fdef->priority = priority;
- fdef->nattrs = nattr;
- fdef->nmatches = nmatch;
- fdef->attrdata = git_buf_detach(&attrs);
-
- filter_def_set_attrs(fdef);
-
- if (git_vector_insert(&filter_registry.filters, fdef) < 0) {
- git__free(fdef->filter_name);
- git__free(fdef->attrdata);
- git__free(fdef);
- return -1;
- }
-
- git_vector_sort(&filter_registry.filters);
- return 0;
-}
-
-int git_filter_global_init(void)
-{
- git_filter *crlf = NULL, *ident = NULL;
- int error = 0;
-
- if (git_rwlock_init(&filter_registry.lock) < 0)
- return -1;
-
- if ((error = git_vector_init(&filter_registry.filters, 2,
- filter_def_priority_cmp)) < 0)
- goto done;
-
- if ((crlf = git_crlf_filter_new()) == NULL ||
- filter_registry_insert(
- GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0 ||
- (ident = git_ident_filter_new()) == NULL ||
- filter_registry_insert(
- GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0)
- error = -1;
-
- git__on_shutdown(git_filter_global_shutdown);
-
-done:
- if (error) {
- git_filter_free(crlf);
- git_filter_free(ident);
- }
-
- return error;
-}
-
-static void git_filter_global_shutdown(void)
-{
- size_t pos;
- git_filter_def *fdef;
-
- if (git_rwlock_wrlock(&filter_registry.lock) < 0)
- return;
-
- git_vector_foreach(&filter_registry.filters, pos, fdef) {
- if (fdef->filter && fdef->filter->shutdown) {
- fdef->filter->shutdown(fdef->filter);
- fdef->initialized = false;
- }
-
- git__free(fdef->filter_name);
- git__free(fdef->attrdata);
- git__free(fdef);
- }
-
- git_vector_free(&filter_registry.filters);
-
- git_rwlock_wrunlock(&filter_registry.lock);
- git_rwlock_free(&filter_registry.lock);
-}
-
-/* Note: callers must lock the registry before calling this function */
-static int filter_registry_find(size_t *pos, const char *name)
-{
- return git_vector_search2(
- pos, &filter_registry.filters, filter_def_name_key_check, name);
-}
-
-/* Note: callers must lock the registry before calling this function */
-static git_filter_def *filter_registry_lookup(size_t *pos, const char *name)
-{
- git_filter_def *fdef = NULL;
-
- if (!filter_registry_find(pos, name))
- fdef = git_vector_get(&filter_registry.filters, *pos);
-
- return fdef;
-}
-
-
-int git_filter_register(
- const char *name, git_filter *filter, int priority)
-{
- int error;
-
- assert(name && filter);
-
- if (git_rwlock_wrlock(&filter_registry.lock) < 0) {
- giterr_set(GITERR_OS, "failed to lock filter registry");
- return -1;
- }
-
- if (!filter_registry_find(NULL, name)) {
- giterr_set(
- GITERR_FILTER, "attempt to reregister existing filter '%s'", name);
- error = GIT_EEXISTS;
- goto done;
- }
-
- error = filter_registry_insert(name, filter, priority);
-
-done:
- git_rwlock_wrunlock(&filter_registry.lock);
- return error;
-}
-
-int git_filter_unregister(const char *name)
-{
- size_t pos;
- git_filter_def *fdef;
- int error = 0;
-
- assert(name);
-
- /* cannot unregister default filters */
- if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) {
- giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name);
- return -1;
- }
-
- if (git_rwlock_wrlock(&filter_registry.lock) < 0) {
- giterr_set(GITERR_OS, "failed to lock filter registry");
- return -1;
- }
-
- if ((fdef = filter_registry_lookup(&pos, name)) == NULL) {
- giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name);
- error = GIT_ENOTFOUND;
- goto done;
- }
-
- git_vector_remove(&filter_registry.filters, pos);
-
- if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
- fdef->filter->shutdown(fdef->filter);
- fdef->initialized = false;
- }
-
- git__free(fdef->filter_name);
- git__free(fdef->attrdata);
- git__free(fdef);
-
-done:
- git_rwlock_wrunlock(&filter_registry.lock);
- return error;
-}
-
-static int filter_initialize(git_filter_def *fdef)
-{
- int error = 0;
-
- if (!fdef->initialized && fdef->filter && fdef->filter->initialize) {
- if ((error = fdef->filter->initialize(fdef->filter)) < 0)
- return error;
- }
-
- fdef->initialized = true;
- return 0;
-}
-
-git_filter *git_filter_lookup(const char *name)
-{
- size_t pos;
- git_filter_def *fdef;
- git_filter *filter = NULL;
-
- if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
- giterr_set(GITERR_OS, "failed to lock filter registry");
- return NULL;
- }
-
- if ((fdef = filter_registry_lookup(&pos, name)) == NULL ||
- (!fdef->initialized && filter_initialize(fdef) < 0))
- goto done;
-
- filter = fdef->filter;
-
-done:
- git_rwlock_rdunlock(&filter_registry.lock);
- return filter;
-}
-
-void git_filter_free(git_filter *filter)
-{
- git__free(filter);
-}
-
-git_repository *git_filter_source_repo(const git_filter_source *src)
-{
- return src->repo;
-}
-
-const char *git_filter_source_path(const git_filter_source *src)
-{
- return src->path;
-}
-
-uint16_t git_filter_source_filemode(const git_filter_source *src)
-{
- return src->filemode;
-}
-
-const git_oid *git_filter_source_id(const git_filter_source *src)
-{
- return git_oid_iszero(&src->oid) ? NULL : &src->oid;
-}
-
-git_filter_mode_t git_filter_source_mode(const git_filter_source *src)
-{
- return src->mode;
-}
-
-uint32_t git_filter_source_flags(const git_filter_source *src)
-{
- return src->flags;
-}
-
-static int filter_list_new(
- git_filter_list **out, const git_filter_source *src)
-{
- git_filter_list *fl = NULL;
- size_t pathlen = src->path ? strlen(src->path) : 0, alloclen;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_filter_list), pathlen);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
-
- fl = git__calloc(1, alloclen);
- GITERR_CHECK_ALLOC(fl);
-
- if (src->path)
- memcpy(fl->path, src->path, pathlen);
- fl->source.repo = src->repo;
- fl->source.path = fl->path;
- fl->source.mode = src->mode;
- fl->source.flags = src->flags;
-
- *out = fl;
- return 0;
-}
-
-static int filter_list_check_attributes(
- const char ***out,
- git_repository *repo,
- git_attr_session *attr_session,
- git_filter_def *fdef,
- const git_filter_source *src)
-{
- int error;
- size_t i;
- const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
- GITERR_CHECK_ALLOC(strs);
-
- error = git_attr_get_many_with_session(
- strs, repo, attr_session, 0, src->path, fdef->nattrs, fdef->attrs);
-
- /* if no values were found but no matches are needed, it's okay! */
- if (error == GIT_ENOTFOUND && !fdef->nmatches) {
- giterr_clear();
- git__free((void *)strs);
- return 0;
- }
-
- for (i = 0; !error && i < fdef->nattrs; ++i) {
- const char *want = fdef->attrs[fdef->nattrs + i];
- git_attr_t want_type, found_type;
-
- if (!want)
- continue;
-
- want_type = git_attr_value(want);
- found_type = git_attr_value(strs[i]);
-
- if (want_type != found_type)
- error = GIT_ENOTFOUND;
- else if (want_type == GIT_ATTR_VALUE_T &&
- strcmp(want, strs[i]) &&
- strcmp(want, "*"))
- error = GIT_ENOTFOUND;
- }
-
- if (error)
- git__free((void *)strs);
- else
- *out = strs;
-
- return error;
-}
-
-int git_filter_list_new(
- git_filter_list **out,
- git_repository *repo,
- git_filter_mode_t mode,
- uint32_t flags)
-{
- git_filter_source src = { 0 };
- src.repo = repo;
- src.path = NULL;
- src.mode = mode;
- src.flags = flags;
- return filter_list_new(out, &src);
-}
-
-int git_filter_list__load_ext(
- git_filter_list **filters,
- git_repository *repo,
- git_blob *blob, /* can be NULL */
- const char *path,
- git_filter_mode_t mode,
- git_filter_options *filter_opts)
-{
- int error = 0;
- git_filter_list *fl = NULL;
- git_filter_source src = { 0 };
- git_filter_entry *fe;
- size_t idx;
- git_filter_def *fdef;
-
- if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
- giterr_set(GITERR_OS, "failed to lock filter registry");
- return -1;
- }
-
- src.repo = repo;
- src.path = path;
- src.mode = mode;
- src.flags = filter_opts->flags;
-
- if (blob)
- git_oid_cpy(&src.oid, git_blob_id(blob));
-
- git_vector_foreach(&filter_registry.filters, idx, fdef) {
- const char **values = NULL;
- void *payload = NULL;
-
- if (!fdef || !fdef->filter)
- continue;
-
- if (fdef->nattrs > 0) {
- error = filter_list_check_attributes(
- &values, repo, filter_opts->attr_session, fdef, &src);
-
- if (error == GIT_ENOTFOUND) {
- error = 0;
- continue;
- } else if (error < 0)
- break;
- }
-
- if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
- break;
-
- if (fdef->filter->check)
- error = fdef->filter->check(
- fdef->filter, &payload, &src, values);
-
- git__free((void *)values);
-
- if (error == GIT_PASSTHROUGH)
- error = 0;
- else if (error < 0)
- break;
- else {
- if (!fl) {
- if ((error = filter_list_new(&fl, &src)) < 0)
- break;
-
- fl->temp_buf = filter_opts->temp_buf;
- }
-
- fe = git_array_alloc(fl->filters);
- GITERR_CHECK_ALLOC(fe);
-
- fe->filter = fdef->filter;
- fe->filter_name = fdef->filter_name;
- fe->payload = payload;
- }
- }
-
- git_rwlock_rdunlock(&filter_registry.lock);
-
- if (error && fl != NULL) {
- git_array_clear(fl->filters);
- git__free(fl);
- fl = NULL;
- }
-
- *filters = fl;
- return error;
-}
-
-int git_filter_list_load(
- git_filter_list **filters,
- git_repository *repo,
- git_blob *blob, /* can be NULL */
- const char *path,
- git_filter_mode_t mode,
- uint32_t flags)
-{
- git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
-
- filter_opts.flags = flags;
-
- return git_filter_list__load_ext(
- filters, repo, blob, path, mode, &filter_opts);
-}
-
-void git_filter_list_free(git_filter_list *fl)
-{
- uint32_t i;
-
- if (!fl)
- return;
-
- for (i = 0; i < git_array_size(fl->filters); ++i) {
- git_filter_entry *fe = git_array_get(fl->filters, i);
- if (fe->filter->cleanup)
- fe->filter->cleanup(fe->filter, fe->payload);
- }
-
- git_array_clear(fl->filters);
- git__free(fl);
-}
-
-int git_filter_list_contains(
- git_filter_list *fl,
- const char *name)
-{
- size_t i;
-
- assert(name);
-
- if (!fl)
- return 0;
-
- for (i = 0; i < fl->filters.size; i++) {
- if (strcmp(fl->filters.ptr[i].filter_name, name) == 0)
- return 1;
- }
-
- return 0;
-}
-
-int git_filter_list_push(
- git_filter_list *fl, git_filter *filter, void *payload)
-{
- int error = 0;
- size_t pos;
- git_filter_def *fdef = NULL;
- git_filter_entry *fe;
-
- assert(fl && filter);
-
- if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
- giterr_set(GITERR_OS, "failed to lock filter registry");
- return -1;
- }
-
- if (git_vector_search2(
- &pos, &filter_registry.filters,
- filter_def_filter_key_check, filter) == 0)
- fdef = git_vector_get(&filter_registry.filters, pos);
-
- git_rwlock_rdunlock(&filter_registry.lock);
-
- if (fdef == NULL) {
- giterr_set(GITERR_FILTER, "Cannot use an unregistered filter");
- return -1;
- }
-
- if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
- return error;
-
- fe = git_array_alloc(fl->filters);
- GITERR_CHECK_ALLOC(fe);
- fe->filter = filter;
- fe->payload = payload;
-
- return 0;
-}
-
-size_t git_filter_list_length(const git_filter_list *fl)
-{
- return fl ? git_array_size(fl->filters) : 0;
-}
-
-struct buf_stream {
- git_writestream parent;
- git_buf *target;
- bool complete;
-};
-
-static int buf_stream_write(
- git_writestream *s, const char *buffer, size_t len)
-{
- struct buf_stream *buf_stream = (struct buf_stream *)s;
- assert(buf_stream);
-
- assert(buf_stream->complete == 0);
-
- return git_buf_put(buf_stream->target, buffer, len);
-}
-
-static int buf_stream_close(git_writestream *s)
-{
- struct buf_stream *buf_stream = (struct buf_stream *)s;
- assert(buf_stream);
-
- assert(buf_stream->complete == 0);
- buf_stream->complete = 1;
-
- return 0;
-}
-
-static void buf_stream_free(git_writestream *s)
-{
- GIT_UNUSED(s);
-}
-
-static void buf_stream_init(struct buf_stream *writer, git_buf *target)
-{
- memset(writer, 0, sizeof(struct buf_stream));
-
- writer->parent.write = buf_stream_write;
- writer->parent.close = buf_stream_close;
- writer->parent.free = buf_stream_free;
- writer->target = target;
-
- git_buf_clear(target);
-}
-
-int git_filter_list_apply_to_data(
- git_buf *tgt, git_filter_list *filters, git_buf *src)
-{
- struct buf_stream writer;
- int error;
-
- git_buf_sanitize(tgt);
- git_buf_sanitize(src);
-
- if (!filters) {
- git_buf_attach_notowned(tgt, src->ptr, src->size);
- return 0;
- }
-
- buf_stream_init(&writer, tgt);
-
- if ((error = git_filter_list_stream_data(filters, src,
- &writer.parent)) < 0)
- return error;
-
- assert(writer.complete);
- return error;
-}
-
-int git_filter_list_apply_to_file(
- git_buf *out,
- git_filter_list *filters,
- git_repository *repo,
- const char *path)
-{
- struct buf_stream writer;
- int error;
-
- buf_stream_init(&writer, out);
-
- if ((error = git_filter_list_stream_file(
- filters, repo, path, &writer.parent)) < 0)
- return error;
-
- assert(writer.complete);
- return error;
-}
-
-static int buf_from_blob(git_buf *out, git_blob *blob)
-{
- git_off_t rawsize = git_blob_rawsize(blob);
-
- if (!git__is_sizet(rawsize)) {
- giterr_set(GITERR_OS, "Blob is too large to filter");
- return -1;
- }
-
- git_buf_attach_notowned(out, git_blob_rawcontent(blob), (size_t)rawsize);
- return 0;
-}
-
-int git_filter_list_apply_to_blob(
- git_buf *out,
- git_filter_list *filters,
- git_blob *blob)
-{
- struct buf_stream writer;
- int error;
-
- buf_stream_init(&writer, out);
-
- if ((error = git_filter_list_stream_blob(
- filters, blob, &writer.parent)) < 0)
- return error;
-
- assert(writer.complete);
- return error;
-}
-
-struct proxy_stream {
- git_writestream parent;
- git_filter *filter;
- const git_filter_source *source;
- void **payload;
- git_buf input;
- git_buf temp_buf;
- git_buf *output;
- git_writestream *target;
-};
-
-static int proxy_stream_write(
- git_writestream *s, const char *buffer, size_t len)
-{
- struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
- assert(proxy_stream);
-
- return git_buf_put(&proxy_stream->input, buffer, len);
-}
-
-static int proxy_stream_close(git_writestream *s)
-{
- struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
- git_buf *writebuf;
- int error;
-
- assert(proxy_stream);
-
- error = proxy_stream->filter->apply(
- proxy_stream->filter,
- proxy_stream->payload,
- proxy_stream->output,
- &proxy_stream->input,
- proxy_stream->source);
-
- if (error == GIT_PASSTHROUGH) {
- writebuf = &proxy_stream->input;
- } else if (error == 0) {
- git_buf_sanitize(proxy_stream->output);
- writebuf = proxy_stream->output;
- } else {
- return error;
- }
-
- if ((error = proxy_stream->target->write(
- proxy_stream->target, writebuf->ptr, writebuf->size)) == 0)
- error = proxy_stream->target->close(proxy_stream->target);
-
- return error;
-}
-
-static void proxy_stream_free(git_writestream *s)
-{
- struct proxy_stream *proxy_stream = (struct proxy_stream *)s;
- assert(proxy_stream);
-
- git_buf_free(&proxy_stream->input);
- git_buf_free(&proxy_stream->temp_buf);
- git__free(proxy_stream);
-}
-
-static int proxy_stream_init(
- git_writestream **out,
- git_filter *filter,
- git_buf *temp_buf,
- void **payload,
- const git_filter_source *source,
- git_writestream *target)
-{
- struct proxy_stream *proxy_stream = git__calloc(1, sizeof(struct proxy_stream));
- GITERR_CHECK_ALLOC(proxy_stream);
-
- proxy_stream->parent.write = proxy_stream_write;
- proxy_stream->parent.close = proxy_stream_close;
- proxy_stream->parent.free = proxy_stream_free;
- proxy_stream->filter = filter;
- proxy_stream->payload = payload;
- proxy_stream->source = source;
- proxy_stream->target = target;
- proxy_stream->output = temp_buf ? temp_buf : &proxy_stream->temp_buf;
-
- if (temp_buf)
- git_buf_clear(temp_buf);
-
- *out = (git_writestream *)proxy_stream;
- return 0;
-}
-
-static int stream_list_init(
- git_writestream **out,
- git_vector *streams,
- git_filter_list *filters,
- git_writestream *target)
-{
- git_writestream *last_stream = target;
- size_t i;
- int error = 0;
-
- *out = NULL;
-
- if (!filters) {
- *out = target;
- return 0;
- }
-
- /* Create filters last to first to get the chaining direction */
- for (i = 0; i < git_array_size(filters->filters); ++i) {
- size_t filter_idx = (filters->source.mode == GIT_FILTER_TO_WORKTREE) ?
- git_array_size(filters->filters) - 1 - i : i;
- git_filter_entry *fe = git_array_get(filters->filters, filter_idx);
- git_writestream *filter_stream;
-
- assert(fe->filter->stream || fe->filter->apply);
-
- /* If necessary, create a stream that proxies the traditional
- * application.
- */
- if (fe->filter->stream)
- error = fe->filter->stream(&filter_stream, fe->filter,
- &fe->payload, &filters->source, last_stream);
- else
- /* Create a stream that proxies the one-shot apply */
- error = proxy_stream_init(&filter_stream, fe->filter,
- filters->temp_buf, &fe->payload, &filters->source,
- last_stream);
-
- if (error < 0)
- return error;
-
- git_vector_insert(streams, filter_stream);
- last_stream = filter_stream;
- }
-
- *out = last_stream;
- return 0;
-}
-
-void stream_list_free(git_vector *streams)
-{
- git_writestream *stream;
- size_t i;
-
- git_vector_foreach(streams, i, stream)
- stream->free(stream);
- git_vector_free(streams);
-}
-
-int git_filter_list_stream_file(
- git_filter_list *filters,
- git_repository *repo,
- const char *path,
- git_writestream *target)
-{
- char buf[FILTERIO_BUFSIZE];
- git_buf abspath = GIT_BUF_INIT;
- const char *base = repo ? git_repository_workdir(repo) : NULL;
- git_vector filter_streams = GIT_VECTOR_INIT;
- git_writestream *stream_start;
- ssize_t readlen;
- int fd = -1, error;
-
- if ((error = stream_list_init(
- &stream_start, &filter_streams, filters, target)) < 0 ||
- (error = git_path_join_unrooted(&abspath, path, base, NULL)) < 0)
- goto done;
-
- if ((fd = git_futils_open_ro(abspath.ptr)) < 0) {
- error = fd;
- goto done;
- }
-
- while ((readlen = p_read(fd, buf, sizeof(buf))) > 0) {
- if ((error = stream_start->write(stream_start, buf, readlen)) < 0)
- goto done;
- }
-
- if (!readlen)
- error = stream_start->close(stream_start);
- else if (readlen < 0)
- error = readlen;
-
-
-done:
- if (fd >= 0)
- p_close(fd);
- stream_list_free(&filter_streams);
- git_buf_free(&abspath);
- return error;
-}
-
-int git_filter_list_stream_data(
- git_filter_list *filters,
- git_buf *data,
- git_writestream *target)
-{
- git_vector filter_streams = GIT_VECTOR_INIT;
- git_writestream *stream_start;
- int error = 0, close_error;
-
- git_buf_sanitize(data);
-
- if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0)
- goto out;
-
- error = stream_start->write(stream_start, data->ptr, data->size);
-
-out:
- close_error = stream_start->close(stream_start);
- stream_list_free(&filter_streams);
- /* propagate the stream init or write error */
- return error < 0 ? error : close_error;
-}
-
-int git_filter_list_stream_blob(
- git_filter_list *filters,
- git_blob *blob,
- git_writestream *target)
-{
- git_buf in = GIT_BUF_INIT;
-
- if (buf_from_blob(&in, blob) < 0)
- return -1;
-
- if (filters)
- git_oid_cpy(&filters->source.oid, git_blob_id(blob));
-
- return git_filter_list_stream_data(filters, &in, target);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_filter_h__
-#define INCLUDE_filter_h__
-
-#include "common.h"
-#include "attr_file.h"
-#include "git2/filter.h"
-
-/* Amount of file to examine for NUL byte when checking binary-ness */
-#define GIT_FILTER_BYTES_TO_CHECK_NUL 8000
-
-/* Possible CRLF values */
-typedef enum {
- GIT_CRLF_GUESS = -1,
- GIT_CRLF_BINARY = 0,
- GIT_CRLF_TEXT,
- GIT_CRLF_INPUT,
- GIT_CRLF_CRLF,
- GIT_CRLF_AUTO,
-} git_crlf_t;
-
-typedef struct {
- git_attr_session *attr_session;
- git_buf *temp_buf;
- uint32_t flags;
-} git_filter_options;
-
-#define GIT_FILTER_OPTIONS_INIT {0}
-
-extern int git_filter_global_init(void);
-
-extern void git_filter_free(git_filter *filter);
-
-extern int git_filter_list__load_ext(
- git_filter_list **filters,
- git_repository *repo,
- git_blob *blob, /* can be NULL */
- const char *path,
- git_filter_mode_t mode,
- git_filter_options *filter_opts);
-
-/*
- * Available filters
- */
-
-extern git_filter *git_crlf_filter_new(void);
-extern git_filter *git_ident_filter_new(void);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-/*
- * This file contains code originally derrived from OpenBSD fnmatch.c
- *
- * Copyright (c) 1989, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Guido van Rossum.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
- * Compares a filename or pathname to a pattern.
- */
-
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "fnmatch.h"
-
-#define EOS '\0'
-
-#define RANGE_MATCH 1
-#define RANGE_NOMATCH 0
-#define RANGE_ERROR (-1)
-
-static int rangematch(const char *, char, int, char **);
-
-static int
-p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs)
-{
- const char *stringstart;
- char *newp;
- char c, test;
- int recurs_flags = flags & ~FNM_PERIOD;
-
- if (recurs-- == 0)
- return FNM_NORES;
-
- for (stringstart = string;;)
- switch (c = *pattern++) {
- case EOS:
- if ((flags & FNM_LEADING_DIR) && *string == '/')
- return (0);
- return (*string == EOS ? 0 : FNM_NOMATCH);
- case '?':
- if (*string == EOS)
- return (FNM_NOMATCH);
- if (*string == '/' && (flags & FNM_PATHNAME))
- return (FNM_NOMATCH);
- if (*string == '.' && (flags & FNM_PERIOD) &&
- (string == stringstart ||
- ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
- ++string;
- break;
- case '*':
- c = *pattern;
-
- /* Let '**' override PATHNAME match for this segment.
- * It will be restored if/when we recurse below.
- */
- if (c == '*') {
- c = *++pattern;
- /* star-star-slash is at the end, match by default */
- if (c == EOS)
- return 0;
- /* Double-star must be at end or between slashes */
- if (c != '/')
- return (FNM_NOMATCH);
-
- c = *++pattern;
- do {
- int e = p_fnmatchx(pattern, string, recurs_flags, recurs);
- if (e != FNM_NOMATCH)
- return e;
- string = strchr(string, '/');
- } while (string++);
-
- /* If we get here, we didn't find a match */
- return FNM_NOMATCH;
- }
-
- if (*string == '.' && (flags & FNM_PERIOD) &&
- (string == stringstart ||
- ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
-
- /* Optimize for pattern with * at end or before /. */
- if (c == EOS) {
- if (flags & FNM_PATHNAME)
- return ((flags & FNM_LEADING_DIR) ||
- strchr(string, '/') == NULL ?
- 0 : FNM_NOMATCH);
- else
- return (0);
- } else if (c == '/' && (flags & FNM_PATHNAME)) {
- if ((string = strchr(string, '/')) == NULL)
- return (FNM_NOMATCH);
- break;
- }
-
- /* General case, use recursion. */
- while ((test = *string) != EOS) {
- int e;
-
- e = p_fnmatchx(pattern, string, recurs_flags, recurs);
- if (e != FNM_NOMATCH)
- return e;
- if (test == '/' && (flags & FNM_PATHNAME))
- break;
- ++string;
- }
- return (FNM_NOMATCH);
- case '[':
- if (*string == EOS)
- return (FNM_NOMATCH);
- if (*string == '/' && (flags & FNM_PATHNAME))
- return (FNM_NOMATCH);
- if (*string == '.' && (flags & FNM_PERIOD) &&
- (string == stringstart ||
- ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
-
- switch (rangematch(pattern, *string, flags, &newp)) {
- case RANGE_ERROR:
- /* not a good range, treat as normal text */
- goto normal;
- case RANGE_MATCH:
- pattern = newp;
- break;
- case RANGE_NOMATCH:
- return (FNM_NOMATCH);
- }
- ++string;
- break;
- case '\\':
- if (!(flags & FNM_NOESCAPE)) {
- if ((c = *pattern++) == EOS) {
- c = '\\';
- --pattern;
- }
- }
- /* FALLTHROUGH */
- default:
- normal:
- if (c != *string && !((flags & FNM_CASEFOLD) &&
- (git__tolower((unsigned char)c) ==
- git__tolower((unsigned char)*string))))
- return (FNM_NOMATCH);
- ++string;
- break;
- }
- /* NOTREACHED */
-}
-
-static int
-rangematch(const char *pattern, char test, int flags, char **newp)
-{
- int negate, ok;
- char c, c2;
-
- /*
- * A bracket expression starting with an unquoted circumflex
- * character produces unspecified results (IEEE 1003.2-1992,
- * 3.13.2). This implementation treats it like '!', for
- * consistency with the regular expression syntax.
- * J.T. Conklin (conklin@ngai.kaleida.com)
- */
- if ((negate = (*pattern == '!' || *pattern == '^')) != 0)
- ++pattern;
-
- if (flags & FNM_CASEFOLD)
- test = (char)git__tolower((unsigned char)test);
-
- /*
- * A right bracket shall lose its special meaning and represent
- * itself in a bracket expression if it occurs first in the list.
- * -- POSIX.2 2.8.3.2
- */
- ok = 0;
- c = *pattern++;
- do {
- if (c == '\\' && !(flags & FNM_NOESCAPE))
- c = *pattern++;
- if (c == EOS)
- return (RANGE_ERROR);
- if (c == '/' && (flags & FNM_PATHNAME))
- return (RANGE_NOMATCH);
- if ((flags & FNM_CASEFOLD))
- c = (char)git__tolower((unsigned char)c);
- if (*pattern == '-'
- && (c2 = *(pattern+1)) != EOS && c2 != ']') {
- pattern += 2;
- if (c2 == '\\' && !(flags & FNM_NOESCAPE))
- c2 = *pattern++;
- if (c2 == EOS)
- return (RANGE_ERROR);
- if (flags & FNM_CASEFOLD)
- c2 = (char)git__tolower((unsigned char)c2);
- if (c <= test && test <= c2)
- ok = 1;
- } else if (c == test)
- ok = 1;
- } while ((c = *pattern++) != ']');
-
- *newp = (char *)pattern;
- return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
-}
-
-int
-p_fnmatch(const char *pattern, const char *string, int flags)
-{
- return p_fnmatchx(pattern, string, flags, 64);
-}
-
+++ /dev/null
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#ifndef INCLUDE_fnmatch__compat_h__
-#define INCLUDE_fnmatch__compat_h__
-
-#include "common.h"
-
-#define FNM_NOMATCH 1 /* Match failed. */
-#define FNM_NOSYS 2 /* Function not supported (unused). */
-#define FNM_NORES 3 /* Out of resources */
-
-#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
-#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
-#define FNM_PERIOD 0x04 /* Period must be matched by period. */
-#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
-#define FNM_CASEFOLD 0x10 /* Case insensitive search. */
-
-#define FNM_IGNORECASE FNM_CASEFOLD
-#define FNM_FILE_NAME FNM_PATHNAME
-
-extern int p_fnmatch(const char *pattern, const char *string, int flags);
-
-#endif /* _FNMATCH_H */
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "global.h"
-#include "hash.h"
-#include "sysdir.h"
-#include "filter.h"
-#include "merge_driver.h"
-#include "openssl_stream.h"
-#include "thread-utils.h"
-#include "git2/global.h"
-#include "transports/ssh.h"
-
-#if defined(GIT_MSVC_CRTDBG)
-#include "win32/w32_stack.h"
-#include "win32/w32_crtdbg_stacktrace.h"
-#endif
-
-git_mutex git__mwindow_mutex;
-
-#define MAX_SHUTDOWN_CB 8
-
-static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
-static git_atomic git__n_shutdown_callbacks;
-static git_atomic git__n_inits;
-char *git__user_agent;
-char *git__ssl_ciphers;
-
-void git__on_shutdown(git_global_shutdown_fn callback)
-{
- int count = git_atomic_inc(&git__n_shutdown_callbacks);
- assert(count <= MAX_SHUTDOWN_CB && count > 0);
- git__shutdown_callbacks[count - 1] = callback;
-}
-
-static void git__global_state_cleanup(git_global_st *st)
-{
- if (!st)
- return;
-
- git__free(st->error_t.message);
- st->error_t.message = NULL;
-}
-
-static int init_common(void)
-{
- int ret;
-
- /* Initialize the CRT debug allocator first, before our first malloc */
-#if defined(GIT_MSVC_CRTDBG)
- git_win32__crtdbg_stacktrace_init();
- git_win32__stack_init();
-#endif
-
- /* Initialize any other subsystems that have global state */
- if ((ret = git_hash_global_init()) == 0 &&
- (ret = git_sysdir_global_init()) == 0 &&
- (ret = git_filter_global_init()) == 0 &&
- (ret = git_merge_driver_global_init()) == 0 &&
- (ret = git_transport_ssh_global_init()) == 0 &&
- (ret = git_openssl_stream_global_init()) == 0)
- ret = git_mwindow_global_init();
-
- GIT_MEMORY_BARRIER;
-
- return ret;
-}
-
-static void shutdown_common(void)
-{
- int pos;
-
- /* Shutdown subsystems that have registered */
- for (pos = git_atomic_get(&git__n_shutdown_callbacks);
- pos > 0;
- pos = git_atomic_dec(&git__n_shutdown_callbacks)) {
-
- git_global_shutdown_fn cb = git__swap(
- git__shutdown_callbacks[pos - 1], NULL);
-
- if (cb != NULL)
- cb();
- }
-
- git__free(git__user_agent);
- git__free(git__ssl_ciphers);
-}
-
-/**
- * Handle the global state with TLS
- *
- * If libgit2 is built with GIT_THREADS enabled,
- * the `git_libgit2_init()` function must be called
- * before calling any other function of the library.
- *
- * This function allocates a TLS index (using pthreads
- * or the native Win32 API) to store the global state
- * on a per-thread basis.
- *
- * Any internal method that requires global state will
- * then call `git__global_state()` which returns a pointer
- * to the global state structure; this pointer is lazily
- * allocated on each thread.
- *
- * Before shutting down the library, the
- * `git_libgit2_shutdown` method must be called to free
- * the previously reserved TLS index.
- *
- * If libgit2 is built without threading support, the
- * `git__global_statestate()` call returns a pointer to a single,
- * statically allocated global state. The `git_thread_`
- * functions are not available in that case.
- */
-
-/*
- * `git_libgit2_init()` allows subsystems to perform global setup,
- * which may take place in the global scope. An explicit memory
- * fence exists at the exit of `git_libgit2_init()`. Without this,
- * CPU cores are free to reorder cache invalidation of `_tls_init`
- * before cache invalidation of the subsystems' newly written global
- * state.
- */
-#if defined(GIT_THREADS) && defined(GIT_WIN32)
-
-static DWORD _tls_index;
-static volatile LONG _mutex = 0;
-
-static int synchronized_threads_init(void)
-{
- int error;
-
- _tls_index = TlsAlloc();
-
- git_threads_init();
-
- if (git_mutex_init(&git__mwindow_mutex))
- return -1;
-
- error = init_common();
-
- return error;
-}
-
-int git_libgit2_init(void)
-{
- int ret;
-
- /* Enter the lock */
- while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
-
- /* Only do work on a 0 -> 1 transition of the refcount */
- if ((ret = git_atomic_inc(&git__n_inits)) == 1) {
- if (synchronized_threads_init() < 0)
- ret = -1;
- }
-
- /* Exit the lock */
- InterlockedExchange(&_mutex, 0);
-
- return ret;
-}
-
-int git_libgit2_shutdown(void)
-{
- int ret;
-
- /* Enter the lock */
- while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
-
- /* Only do work on a 1 -> 0 transition of the refcount */
- if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
- shutdown_common();
-
- git__free_tls_data();
-
- TlsFree(_tls_index);
- git_mutex_free(&git__mwindow_mutex);
-
-#if defined(GIT_MSVC_CRTDBG)
- git_win32__crtdbg_stacktrace_cleanup();
- git_win32__stack_cleanup();
-#endif
- }
-
- /* Exit the lock */
- InterlockedExchange(&_mutex, 0);
-
- return ret;
-}
-
-git_global_st *git__global_state(void)
-{
- git_global_st *ptr;
-
- assert(git_atomic_get(&git__n_inits) > 0);
-
- if ((ptr = TlsGetValue(_tls_index)) != NULL)
- return ptr;
-
- ptr = git__calloc(1, sizeof(git_global_st));
- if (!ptr)
- return NULL;
-
- git_buf_init(&ptr->error_buf, 0);
-
- TlsSetValue(_tls_index, ptr);
- return ptr;
-}
-
-/**
- * Free the TLS data associated with this thread.
- * This should only be used by the thread as it
- * is exiting.
- */
-void git__free_tls_data(void)
-{
- void *ptr = TlsGetValue(_tls_index);
- if (!ptr)
- return;
-
- git__global_state_cleanup(ptr);
- git__free(ptr);
- TlsSetValue(_tls_index, NULL);
-}
-
-BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
-{
- GIT_UNUSED(hInstDll);
- GIT_UNUSED(lpvReserved);
-
- /* This is how Windows lets us know our thread is being shut down */
- if (fdwReason == DLL_THREAD_DETACH) {
- git__free_tls_data();
- }
-
- /*
- * Windows pays attention to this during library loading. We don't do anything
- * so we trivially succeed.
- */
- return TRUE;
-}
-
-#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
-
-static pthread_key_t _tls_key;
-static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
-int init_error = 0;
-
-static void cb__free_status(void *st)
-{
- git__global_state_cleanup(st);
- git__free(st);
-}
-
-static void init_once(void)
-{
- if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
- return;
-
- pthread_key_create(&_tls_key, &cb__free_status);
-
- init_error = init_common();
-}
-
-int git_libgit2_init(void)
-{
- int ret, err;
-
- ret = git_atomic_inc(&git__n_inits);
-
- if ((err = pthread_mutex_lock(&_init_mutex)) != 0)
- return err;
- err = pthread_once(&_once_init, init_once);
- err |= pthread_mutex_unlock(&_init_mutex);
-
- if (err || init_error)
- return err | init_error;
-
- return ret;
-}
-
-int git_libgit2_shutdown(void)
-{
- void *ptr = NULL;
- pthread_once_t new_once = PTHREAD_ONCE_INIT;
- int ret;
-
- if ((ret = git_atomic_dec(&git__n_inits)) != 0)
- return ret;
-
- if ((ret = pthread_mutex_lock(&_init_mutex)) != 0)
- return ret;
-
- /* Shut down any subsystems that have global state */
- shutdown_common();
-
- ptr = pthread_getspecific(_tls_key);
- pthread_setspecific(_tls_key, NULL);
-
- git__global_state_cleanup(ptr);
- git__free(ptr);
-
- pthread_key_delete(_tls_key);
- git_mutex_free(&git__mwindow_mutex);
- _once_init = new_once;
-
- if ((ret = pthread_mutex_unlock(&_init_mutex)) != 0)
- return ret;
-
- return 0;
-}
-
-git_global_st *git__global_state(void)
-{
- git_global_st *ptr;
-
- assert(git_atomic_get(&git__n_inits) > 0);
-
- if ((ptr = pthread_getspecific(_tls_key)) != NULL)
- return ptr;
-
- ptr = git__calloc(1, sizeof(git_global_st));
- if (!ptr)
- return NULL;
-
- git_buf_init(&ptr->error_buf, 0);
- pthread_setspecific(_tls_key, ptr);
- return ptr;
-}
-
-#else
-
-static git_global_st __state;
-
-int git_libgit2_init(void)
-{
- int ret;
-
- /* Only init subsystems the first time */
- if ((ret = git_atomic_inc(&git__n_inits)) != 1)
- return ret;
-
- if ((ret = init_common()) < 0)
- return ret;
-
- return 1;
-}
-
-int git_libgit2_shutdown(void)
-{
- int ret;
-
- /* Shut down any subsystems that have global state */
- if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
- shutdown_common();
- git__global_state_cleanup(&__state);
- memset(&__state, 0, sizeof(__state));
- }
-
- return ret;
-}
-
-git_global_st *git__global_state(void)
-{
- return &__state;
-}
-
-#endif /* GIT_THREADS */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_global_h__
-#define INCLUDE_global_h__
-
-#include "common.h"
-#include "mwindow.h"
-#include "hash.h"
-
-typedef struct {
- git_error *last_error;
- git_error error_t;
- git_buf error_buf;
- char oid_fmt[GIT_OID_HEXSZ+1];
-
- /* On Windows, this is the current child thread that was started by
- * `git_thread_create`. This is used to set the thread's exit code
- * when terminated by `git_thread_exit`. It is unused on POSIX.
- */
- git_thread *current_thread;
-} git_global_st;
-
-#ifdef GIT_OPENSSL
-# include <openssl/ssl.h>
-extern SSL_CTX *git__ssl_ctx;
-#endif
-
-git_global_st *git__global_state(void);
-
-extern git_mutex git__mwindow_mutex;
-
-#define GIT_GLOBAL (git__global_state())
-
-typedef void (*git_global_shutdown_fn)(void);
-
-extern void git__on_shutdown(git_global_shutdown_fn callback);
-
-extern void git__free_tls_data(void);
-
-extern const char *git_libgit2__user_agent(void);
-extern const char *git_libgit2__ssl_ciphers(void);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "revwalk.h"
-#include "merge.h"
-#include "git2/graph.h"
-
-static int interesting(git_pqueue *list, git_commit_list *roots)
-{
- unsigned int i;
-
- for (i = 0; i < git_pqueue_size(list); i++) {
- git_commit_list_node *commit = git_pqueue_get(list, i);
- if ((commit->flags & STALE) == 0)
- return 1;
- }
-
- while(roots) {
- if ((roots->item->flags & STALE) == 0)
- return 1;
- roots = roots->next;
- }
-
- return 0;
-}
-
-static int mark_parents(git_revwalk *walk, git_commit_list_node *one,
- git_commit_list_node *two)
-{
- unsigned int i;
- git_commit_list *roots = NULL;
- git_pqueue list;
-
- /* if the commit is repeated, we have a our merge base already */
- if (one == two) {
- one->flags |= PARENT1 | PARENT2 | RESULT;
- return 0;
- }
-
- if (git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp) < 0)
- return -1;
-
- if (git_commit_list_parse(walk, one) < 0)
- goto on_error;
- one->flags |= PARENT1;
- if (git_pqueue_insert(&list, one) < 0)
- goto on_error;
-
- if (git_commit_list_parse(walk, two) < 0)
- goto on_error;
- two->flags |= PARENT2;
- if (git_pqueue_insert(&list, two) < 0)
- goto on_error;
-
- /* as long as there are non-STALE commits */
- while (interesting(&list, roots)) {
- git_commit_list_node *commit = git_pqueue_pop(&list);
- unsigned int flags;
-
- if (commit == NULL)
- break;
-
- flags = commit->flags & (PARENT1 | PARENT2 | STALE);
- if (flags == (PARENT1 | PARENT2)) {
- if (!(commit->flags & RESULT))
- commit->flags |= RESULT;
- /* we mark the parents of a merge stale */
- flags |= STALE;
- }
-
- for (i = 0; i < commit->out_degree; i++) {
- git_commit_list_node *p = commit->parents[i];
- if ((p->flags & flags) == flags)
- continue;
-
- if (git_commit_list_parse(walk, p) < 0)
- goto on_error;
-
- p->flags |= flags;
- if (git_pqueue_insert(&list, p) < 0)
- goto on_error;
- }
-
- /* Keep track of root commits, to make sure the path gets marked */
- if (commit->out_degree == 0) {
- if (git_commit_list_insert(commit, &roots) == NULL)
- goto on_error;
- }
- }
-
- git_commit_list_free(&roots);
- git_pqueue_free(&list);
- return 0;
-
-on_error:
- git_commit_list_free(&roots);
- git_pqueue_free(&list);
- return -1;
-}
-
-
-static int ahead_behind(git_commit_list_node *one, git_commit_list_node *two,
- size_t *ahead, size_t *behind)
-{
- git_commit_list_node *commit;
- git_pqueue pq;
- int error = 0, i;
- *ahead = 0;
- *behind = 0;
-
- if (git_pqueue_init(&pq, 0, 2, git_commit_list_time_cmp) < 0)
- return -1;
-
- if ((error = git_pqueue_insert(&pq, one)) < 0 ||
- (error = git_pqueue_insert(&pq, two)) < 0)
- goto done;
-
- while ((commit = git_pqueue_pop(&pq)) != NULL) {
- if (commit->flags & RESULT ||
- (commit->flags & (PARENT1 | PARENT2)) == (PARENT1 | PARENT2))
- continue;
- else if (commit->flags & PARENT1)
- (*ahead)++;
- else if (commit->flags & PARENT2)
- (*behind)++;
-
- for (i = 0; i < commit->out_degree; i++) {
- git_commit_list_node *p = commit->parents[i];
- if ((error = git_pqueue_insert(&pq, p)) < 0)
- goto done;
- }
- commit->flags |= RESULT;
- }
-
-done:
- git_pqueue_free(&pq);
- return error;
-}
-
-int git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo,
- const git_oid *local, const git_oid *upstream)
-{
- git_revwalk *walk;
- git_commit_list_node *commit_u, *commit_l;
-
- if (git_revwalk_new(&walk, repo) < 0)
- return -1;
-
- commit_u = git_revwalk__commit_lookup(walk, upstream);
- if (commit_u == NULL)
- goto on_error;
-
- commit_l = git_revwalk__commit_lookup(walk, local);
- if (commit_l == NULL)
- goto on_error;
-
- if (mark_parents(walk, commit_l, commit_u) < 0)
- goto on_error;
- if (ahead_behind(commit_l, commit_u, ahead, behind) < 0)
- goto on_error;
-
- git_revwalk_free(walk);
-
- return 0;
-
-on_error:
- git_revwalk_free(walk);
- return -1;
-}
-
-int git_graph_descendant_of(git_repository *repo, const git_oid *commit, const git_oid *ancestor)
-{
- git_oid merge_base;
- int error;
-
- if (git_oid_equal(commit, ancestor))
- return 0;
-
- error = git_merge_base(&merge_base, repo, commit, ancestor);
- /* No merge-base found, it's not a descendant */
- if (error == GIT_ENOTFOUND)
- return 0;
-
- if (error < 0)
- return error;
-
- return git_oid_equal(&merge_base, ancestor);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "hash.h"
-
-int git_hash_buf(git_oid *out, const void *data, size_t len)
-{
- git_hash_ctx ctx;
- int error = 0;
-
- if (git_hash_ctx_init(&ctx) < 0)
- return -1;
-
- if ((error = git_hash_update(&ctx, data, len)) >= 0)
- error = git_hash_final(out, &ctx);
-
- git_hash_ctx_cleanup(&ctx);
-
- return error;
-}
-
-int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n)
-{
- git_hash_ctx ctx;
- size_t i;
- int error = 0;
-
- if (git_hash_ctx_init(&ctx) < 0)
- return -1;
-
- for (i = 0; i < n; i++) {
- if ((error = git_hash_update(&ctx, vec[i].data, vec[i].len)) < 0)
- goto done;
- }
-
- error = git_hash_final(out, &ctx);
-
-done:
- git_hash_ctx_cleanup(&ctx);
-
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_hash_h__
-#define INCLUDE_hash_h__
-
-#include "git2/oid.h"
-
-typedef struct git_hash_prov git_hash_prov;
-typedef struct git_hash_ctx git_hash_ctx;
-
-int git_hash_global_init(void);
-int git_hash_ctx_init(git_hash_ctx *ctx);
-void git_hash_ctx_cleanup(git_hash_ctx *ctx);
-
-#if defined(GIT_COMMON_CRYPTO)
-# include "hash/hash_common_crypto.h"
-#elif defined(OPENSSL_SHA1)
-# include "hash/hash_openssl.h"
-#elif defined(WIN32_SHA1)
-# include "hash/hash_win32.h"
-#else
-# include "hash/hash_generic.h"
-#endif
-
-typedef struct {
- void *data;
- size_t len;
-} git_buf_vec;
-
-int git_hash_init(git_hash_ctx *c);
-int git_hash_update(git_hash_ctx *c, const void *data, size_t len);
-int git_hash_final(git_oid *out, git_hash_ctx *c);
-
-int git_hash_buf(git_oid *out, const void *data, size_t len);
-int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n);
-
-#endif /* INCLUDE_hash_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_hash_common_crypto_h__
-#define INCLUDE_hash_common_crypto_h__
-
-#include "hash.h"
-
-#include <CommonCrypto/CommonDigest.h>
-
-struct git_hash_ctx {
- CC_SHA1_CTX c;
-};
-
-#define git_hash_global_init() 0
-#define git_hash_ctx_init(ctx) git_hash_init(ctx)
-#define git_hash_ctx_cleanup(ctx)
-
-GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx)
-{
- assert(ctx);
- CC_SHA1_Init(&ctx->c);
- return 0;
-}
-
-GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
-{
- assert(ctx);
- CC_SHA1_Update(&ctx->c, data, len);
- return 0;
-}
-
-GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx)
-{
- assert(ctx);
- CC_SHA1_Final(out->id, &ctx->c);
- return 0;
-}
-
-#endif /* INCLUDE_hash_common_crypto_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "hash.h"
-#include "hash/hash_generic.h"
-
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-
-/*
- * Force usage of rol or ror by selecting the one with the smaller constant.
- * It _can_ generate slightly smaller code (a constant of 1 is special), but
- * perhaps more importantly it's possibly faster on any uarch that does a
- * rotate with a loop.
- */
-
-#define SHA_ASM(op, x, n) (__extension__ ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }))
-#define SHA_ROL(x,n) SHA_ASM("rol", x, n)
-#define SHA_ROR(x,n) SHA_ASM("ror", x, n)
-
-#else
-
-#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r)))
-#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n))
-#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n)
-
-#endif
-
-/*
- * If you have 32 registers or more, the compiler can (and should)
- * try to change the array[] accesses into registers. However, on
- * machines with less than ~25 registers, that won't really work,
- * and at least gcc will make an unholy mess of it.
- *
- * So to avoid that mess which just slows things down, we force
- * the stores to memory to actually happen (we might be better off
- * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
- * suggested by Artur Skawina - that will also make gcc unable to
- * try to do the silly "optimize away loads" part because it won't
- * see what the value will be).
- *
- * Ben Herrenschmidt reports that on PPC, the C version comes close
- * to the optimized asm with this (ie on PPC you don't want that
- * 'volatile', since there are lots of registers).
- *
- * On ARM we get the best code generation by forcing a full memory barrier
- * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
- * the stack frame size simply explode and performance goes down the drain.
- */
-
-#if defined(__i386__) || defined(__x86_64__)
- #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
-#elif defined(__GNUC__) && defined(__arm__)
- #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
-#else
- #define setW(x, val) (W(x) = (val))
-#endif
-
-/*
- * Performance might be improved if the CPU architecture is OK with
- * unaligned 32-bit loads and a fast ntohl() is available.
- * Otherwise fall back to byte loads and shifts which is portable,
- * and is faster on architectures with memory alignment issues.
- */
-
-#if defined(__i386__) || defined(__x86_64__) || \
- defined(_M_IX86) || defined(_M_X64) || \
- defined(__ppc__) || defined(__ppc64__) || \
- defined(__powerpc__) || defined(__powerpc64__) || \
- defined(__s390__) || defined(__s390x__)
-
-#define get_be32(p) ntohl(*(const unsigned int *)(p))
-#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
-
-#else
-
-#define get_be32(p) ( \
- (*((const unsigned char *)(p) + 0) << 24) | \
- (*((const unsigned char *)(p) + 1) << 16) | \
- (*((const unsigned char *)(p) + 2) << 8) | \
- (*((const unsigned char *)(p) + 3) << 0) )
-#define put_be32(p, v) do { \
- unsigned int __v = (v); \
- *((unsigned char *)(p) + 0) = __v >> 24; \
- *((unsigned char *)(p) + 1) = __v >> 16; \
- *((unsigned char *)(p) + 2) = __v >> 8; \
- *((unsigned char *)(p) + 3) = __v >> 0; } while (0)
-
-#endif
-
-/* This "rolls" over the 512-bit array */
-#define W(x) (array[(x)&15])
-
-/*
- * Where do we get the source from? The first 16 iterations get it from
- * the input data, the next mix it from the 512-bit array.
- */
-#define SHA_SRC(t) get_be32(data + t)
-#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
-
-#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
- unsigned int TEMP = input(t); setW(t, TEMP); \
- E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \
- B = SHA_ROR(B, 2); } while (0)
-
-#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
-#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
-#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
-#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
-#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )
-
-static void hash__block(git_hash_ctx *ctx, const unsigned int *data)
-{
- unsigned int A,B,C,D,E;
- unsigned int array[16];
-
- A = ctx->H[0];
- B = ctx->H[1];
- C = ctx->H[2];
- D = ctx->H[3];
- E = ctx->H[4];
-
- /* Round 1 - iterations 0-16 take their input from 'data' */
- T_0_15( 0, A, B, C, D, E);
- T_0_15( 1, E, A, B, C, D);
- T_0_15( 2, D, E, A, B, C);
- T_0_15( 3, C, D, E, A, B);
- T_0_15( 4, B, C, D, E, A);
- T_0_15( 5, A, B, C, D, E);
- T_0_15( 6, E, A, B, C, D);
- T_0_15( 7, D, E, A, B, C);
- T_0_15( 8, C, D, E, A, B);
- T_0_15( 9, B, C, D, E, A);
- T_0_15(10, A, B, C, D, E);
- T_0_15(11, E, A, B, C, D);
- T_0_15(12, D, E, A, B, C);
- T_0_15(13, C, D, E, A, B);
- T_0_15(14, B, C, D, E, A);
- T_0_15(15, A, B, C, D, E);
-
- /* Round 1 - tail. Input from 512-bit mixing array */
- T_16_19(16, E, A, B, C, D);
- T_16_19(17, D, E, A, B, C);
- T_16_19(18, C, D, E, A, B);
- T_16_19(19, B, C, D, E, A);
-
- /* Round 2 */
- T_20_39(20, A, B, C, D, E);
- T_20_39(21, E, A, B, C, D);
- T_20_39(22, D, E, A, B, C);
- T_20_39(23, C, D, E, A, B);
- T_20_39(24, B, C, D, E, A);
- T_20_39(25, A, B, C, D, E);
- T_20_39(26, E, A, B, C, D);
- T_20_39(27, D, E, A, B, C);
- T_20_39(28, C, D, E, A, B);
- T_20_39(29, B, C, D, E, A);
- T_20_39(30, A, B, C, D, E);
- T_20_39(31, E, A, B, C, D);
- T_20_39(32, D, E, A, B, C);
- T_20_39(33, C, D, E, A, B);
- T_20_39(34, B, C, D, E, A);
- T_20_39(35, A, B, C, D, E);
- T_20_39(36, E, A, B, C, D);
- T_20_39(37, D, E, A, B, C);
- T_20_39(38, C, D, E, A, B);
- T_20_39(39, B, C, D, E, A);
-
- /* Round 3 */
- T_40_59(40, A, B, C, D, E);
- T_40_59(41, E, A, B, C, D);
- T_40_59(42, D, E, A, B, C);
- T_40_59(43, C, D, E, A, B);
- T_40_59(44, B, C, D, E, A);
- T_40_59(45, A, B, C, D, E);
- T_40_59(46, E, A, B, C, D);
- T_40_59(47, D, E, A, B, C);
- T_40_59(48, C, D, E, A, B);
- T_40_59(49, B, C, D, E, A);
- T_40_59(50, A, B, C, D, E);
- T_40_59(51, E, A, B, C, D);
- T_40_59(52, D, E, A, B, C);
- T_40_59(53, C, D, E, A, B);
- T_40_59(54, B, C, D, E, A);
- T_40_59(55, A, B, C, D, E);
- T_40_59(56, E, A, B, C, D);
- T_40_59(57, D, E, A, B, C);
- T_40_59(58, C, D, E, A, B);
- T_40_59(59, B, C, D, E, A);
-
- /* Round 4 */
- T_60_79(60, A, B, C, D, E);
- T_60_79(61, E, A, B, C, D);
- T_60_79(62, D, E, A, B, C);
- T_60_79(63, C, D, E, A, B);
- T_60_79(64, B, C, D, E, A);
- T_60_79(65, A, B, C, D, E);
- T_60_79(66, E, A, B, C, D);
- T_60_79(67, D, E, A, B, C);
- T_60_79(68, C, D, E, A, B);
- T_60_79(69, B, C, D, E, A);
- T_60_79(70, A, B, C, D, E);
- T_60_79(71, E, A, B, C, D);
- T_60_79(72, D, E, A, B, C);
- T_60_79(73, C, D, E, A, B);
- T_60_79(74, B, C, D, E, A);
- T_60_79(75, A, B, C, D, E);
- T_60_79(76, E, A, B, C, D);
- T_60_79(77, D, E, A, B, C);
- T_60_79(78, C, D, E, A, B);
- T_60_79(79, B, C, D, E, A);
-
- ctx->H[0] += A;
- ctx->H[1] += B;
- ctx->H[2] += C;
- ctx->H[3] += D;
- ctx->H[4] += E;
-}
-
-int git_hash_init(git_hash_ctx *ctx)
-{
- ctx->size = 0;
-
- /* Initialize H with the magic constants (see FIPS180 for constants) */
- ctx->H[0] = 0x67452301;
- ctx->H[1] = 0xefcdab89;
- ctx->H[2] = 0x98badcfe;
- ctx->H[3] = 0x10325476;
- ctx->H[4] = 0xc3d2e1f0;
-
- return 0;
-}
-
-int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
-{
- unsigned int lenW = ctx->size & 63;
-
- ctx->size += len;
-
- /* Read the data into W and process blocks as they get full */
- if (lenW) {
- unsigned int left = 64 - lenW;
- if (len < left)
- left = (unsigned int)len;
- memcpy(lenW + (char *)ctx->W, data, left);
- lenW = (lenW + left) & 63;
- len -= left;
- data = ((const char *)data + left);
- if (lenW)
- return 0;
- hash__block(ctx, ctx->W);
- }
- while (len >= 64) {
- hash__block(ctx, data);
- data = ((const char *)data + 64);
- len -= 64;
- }
- if (len)
- memcpy(ctx->W, data, len);
-
- return 0;
-}
-
-int git_hash_final(git_oid *out, git_hash_ctx *ctx)
-{
- static const unsigned char pad[64] = { 0x80 };
- unsigned int padlen[2];
- int i;
-
- /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
- padlen[0] = htonl((uint32_t)(ctx->size >> 29));
- padlen[1] = htonl((uint32_t)(ctx->size << 3));
-
- i = ctx->size & 63;
- git_hash_update(ctx, pad, 1+ (63 & (55 - i)));
- git_hash_update(ctx, padlen, 8);
-
- /* Output hash */
- for (i = 0; i < 5; i++)
- put_be32(out->id + i*4, ctx->H[i]);
-
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_hash_generic_h__
-#define INCLUDE_hash_generic_h__
-
-#include "hash.h"
-
-struct git_hash_ctx {
- unsigned long long size;
- unsigned int H[5];
- unsigned int W[16];
-};
-
-#define git_hash_global_init() 0
-#define git_hash_ctx_init(ctx) git_hash_init(ctx)
-#define git_hash_ctx_cleanup(ctx)
-
-#endif /* INCLUDE_hash_generic_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_hash_openssl_h__
-#define INCLUDE_hash_openssl_h__
-
-#include "hash.h"
-
-#include <openssl/sha.h>
-
-struct git_hash_ctx {
- SHA_CTX c;
-};
-
-#define git_hash_global_init() 0
-#define git_hash_ctx_init(ctx) git_hash_init(ctx)
-#define git_hash_ctx_cleanup(ctx)
-
-GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx)
-{
- assert(ctx);
- SHA1_Init(&ctx->c);
- return 0;
-}
-
-GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
-{
- assert(ctx);
- SHA1_Update(&ctx->c, data, len);
- return 0;
-}
-
-GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx)
-{
- assert(ctx);
- SHA1_Final(out->id, &ctx->c);
- return 0;
-}
-
-#endif /* INCLUDE_hash_openssl_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "global.h"
-#include "hash.h"
-#include "hash/hash_win32.h"
-
-#include <wincrypt.h>
-#include <strsafe.h>
-
-static struct git_hash_prov hash_prov = {0};
-
-/* Hash initialization */
-
-/* Initialize CNG, if available */
-GIT_INLINE(int) hash_cng_prov_init(void)
-{
- char dll_path[MAX_PATH];
- DWORD dll_path_len, size_len;
-
- /* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
- if (!git_has_win32_version(6, 0, 1))
- return -1;
-
- /* Load bcrypt.dll explicitly from the system directory */
- if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 ||
- dll_path_len > MAX_PATH ||
- StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
- StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
- (hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL)
- return -1;
-
- /* Load the function addresses */
- if ((hash_prov.prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL ||
- (hash_prov.prov.cng.get_property = (hash_win32_cng_get_property_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptGetProperty")) == NULL ||
- (hash_prov.prov.cng.create_hash = (hash_win32_cng_create_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCreateHash")) == NULL ||
- (hash_prov.prov.cng.finish_hash = (hash_win32_cng_finish_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptFinishHash")) == NULL ||
- (hash_prov.prov.cng.hash_data = (hash_win32_cng_hash_data_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptHashData")) == NULL ||
- (hash_prov.prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptDestroyHash")) == NULL ||
- (hash_prov.prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) {
- FreeLibrary(hash_prov.prov.cng.dll);
- return -1;
- }
-
- /* Load the SHA1 algorithm */
- if (hash_prov.prov.cng.open_algorithm_provider(&hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) {
- FreeLibrary(hash_prov.prov.cng.dll);
- return -1;
- }
-
- /* Get storage space for the hash object */
- if (hash_prov.prov.cng.get_property(hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_prov.prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) {
- hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
- FreeLibrary(hash_prov.prov.cng.dll);
- return -1;
- }
-
- hash_prov.type = CNG;
- return 0;
-}
-
-GIT_INLINE(void) hash_cng_prov_shutdown(void)
-{
- hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
- FreeLibrary(hash_prov.prov.cng.dll);
-
- hash_prov.type = INVALID;
-}
-
-/* Initialize CryptoAPI */
-GIT_INLINE(int) hash_cryptoapi_prov_init()
-{
- if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
- return -1;
-
- hash_prov.type = CRYPTOAPI;
- return 0;
-}
-
-GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
-{
- CryptReleaseContext(hash_prov.prov.cryptoapi.handle, 0);
-
- hash_prov.type = INVALID;
-}
-
-static void git_hash_global_shutdown(void)
-{
- if (hash_prov.type == CNG)
- hash_cng_prov_shutdown();
- else if(hash_prov.type == CRYPTOAPI)
- hash_cryptoapi_prov_shutdown();
-}
-
-int git_hash_global_init(void)
-{
- int error = 0;
-
- if (hash_prov.type != INVALID)
- return 0;
-
- if ((error = hash_cng_prov_init()) < 0)
- error = hash_cryptoapi_prov_init();
-
- git__on_shutdown(git_hash_global_shutdown);
-
- return error;
-}
-
-/* CryptoAPI: available in Windows XP and newer */
-
-GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_ctx *ctx)
-{
- ctx->type = CRYPTOAPI;
- ctx->prov = &hash_prov;
-
- return git_hash_init(ctx);
-}
-
-GIT_INLINE(int) hash_cryptoapi_init(git_hash_ctx *ctx)
-{
- if (ctx->ctx.cryptoapi.valid)
- CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
-
- if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
- ctx->ctx.cryptoapi.valid = 0;
- return -1;
- }
-
- ctx->ctx.cryptoapi.valid = 1;
- return 0;
-}
-
-GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *data, size_t len)
-{
- assert(ctx->ctx.cryptoapi.valid);
-
- if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, (const BYTE *)data, (DWORD)len, 0))
- return -1;
-
- return 0;
-}
-
-GIT_INLINE(int) hash_cryptoapi_final(git_oid *out, git_hash_ctx *ctx)
-{
- DWORD len = 20;
- int error = 0;
-
- assert(ctx->ctx.cryptoapi.valid);
-
- if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out->id, &len, 0))
- error = -1;
-
- CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
- ctx->ctx.cryptoapi.valid = 0;
-
- return error;
-}
-
-GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_ctx *ctx)
-{
- if (ctx->ctx.cryptoapi.valid)
- CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
-}
-
-/* CNG: Available in Windows Server 2008 and newer */
-
-GIT_INLINE(int) hash_ctx_cng_init(git_hash_ctx *ctx)
-{
- if ((ctx->ctx.cng.hash_object = git__malloc(hash_prov.prov.cng.hash_object_size)) == NULL)
- return -1;
-
- if (hash_prov.prov.cng.create_hash(hash_prov.prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_prov.prov.cng.hash_object_size, NULL, 0, 0) < 0) {
- git__free(ctx->ctx.cng.hash_object);
- return -1;
- }
-
- ctx->type = CNG;
- ctx->prov = &hash_prov;
-
- return 0;
-}
-
-GIT_INLINE(int) hash_cng_init(git_hash_ctx *ctx)
-{
- BYTE hash[GIT_OID_RAWSZ];
-
- if (!ctx->ctx.cng.updated)
- return 0;
-
- /* CNG needs to be finished to restart */
- if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0)
- return -1;
-
- ctx->ctx.cng.updated = 0;
-
- return 0;
-}
-
-GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *data, size_t len)
-{
- if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, (PBYTE)data, (ULONG)len, 0) < 0)
- return -1;
-
- return 0;
-}
-
-GIT_INLINE(int) hash_cng_final(git_oid *out, git_hash_ctx *ctx)
-{
- if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out->id, GIT_OID_RAWSZ, 0) < 0)
- return -1;
-
- ctx->ctx.cng.updated = 0;
-
- return 0;
-}
-
-GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_ctx *ctx)
-{
- ctx->prov->prov.cng.destroy_hash(ctx->ctx.cng.hash_handle);
- git__free(ctx->ctx.cng.hash_object);
-}
-
-/* Indirection between CryptoAPI and CNG */
-
-int git_hash_ctx_init(git_hash_ctx *ctx)
-{
- int error = 0;
-
- assert(ctx);
-
- /*
- * When compiled with GIT_THREADS, the global hash_prov data is
- * initialized with git_libgit2_init. Otherwise, it must be initialized
- * at first use.
- */
- if (hash_prov.type == INVALID && (error = git_hash_global_init()) < 0)
- return error;
-
- memset(ctx, 0x0, sizeof(git_hash_ctx));
-
- return (hash_prov.type == CNG) ? hash_ctx_cng_init(ctx) : hash_ctx_cryptoapi_init(ctx);
-}
-
-int git_hash_init(git_hash_ctx *ctx)
-{
- assert(ctx && ctx->type);
- return (ctx->type == CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx);
-}
-
-int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
-{
- assert(ctx && ctx->type);
- return (ctx->type == CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len);
-}
-
-int git_hash_final(git_oid *out, git_hash_ctx *ctx)
-{
- assert(ctx && ctx->type);
- return (ctx->type == CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx);
-}
-
-void git_hash_ctx_cleanup(git_hash_ctx *ctx)
-{
- assert(ctx);
-
- if (ctx->type == CNG)
- hash_ctx_cng_cleanup(ctx);
- else if(ctx->type == CRYPTOAPI)
- hash_ctx_cryptoapi_cleanup(ctx);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_hash_win32_h__
-#define INCLUDE_hash_win32_h__
-
-#include "common.h"
-#include "hash.h"
-
-#include <wincrypt.h>
-#include <strsafe.h>
-
-enum hash_win32_prov_type {
- INVALID = 0,
- CRYPTOAPI,
- CNG
-};
-
-/*
- * CryptoAPI is available for hashing on Windows XP and newer.
- */
-
-struct hash_cryptoapi_prov {
- HCRYPTPROV handle;
-};
-
-/*
- * CNG (bcrypt.dll) is significantly more performant than CryptoAPI and is
- * preferred, however it is only available on Windows 2008 and newer and
- * must therefore be dynamically loaded, and we must inline constants that
- * would not exist when building in pre-Windows 2008 environments.
- */
-
-#define GIT_HASH_CNG_DLL_NAME "bcrypt.dll"
-
-/* BCRYPT_SHA1_ALGORITHM */
-#define GIT_HASH_CNG_HASH_TYPE L"SHA1"
-
-/* BCRYPT_OBJECT_LENGTH */
-#define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength"
-
-/* BCRYPT_HASH_REUSEABLE_FLAGS */
-#define GIT_HASH_CNG_HASH_REUSABLE 0x00000020
-
-/* Function declarations for CNG */
-typedef NTSTATUS (WINAPI *hash_win32_cng_open_algorithm_provider_fn)(
- HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm,
- LPCWSTR pszAlgId,
- LPCWSTR pszImplementation,
- DWORD dwFlags);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_get_property_fn)(
- HANDLE /* BCRYPT_HANDLE */ hObject,
- LPCWSTR pszProperty,
- PUCHAR pbOutput,
- ULONG cbOutput,
- ULONG *pcbResult,
- ULONG dwFlags);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_create_hash_fn)(
- HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
- HANDLE /* BCRYPT_HASH_HANDLE */ *phHash,
- PUCHAR pbHashObject, ULONG cbHashObject,
- PUCHAR pbSecret,
- ULONG cbSecret,
- ULONG dwFlags);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_finish_hash_fn)(
- HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
- PUCHAR pbOutput,
- ULONG cbOutput,
- ULONG dwFlags);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_hash_data_fn)(
- HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
- PUCHAR pbInput,
- ULONG cbInput,
- ULONG dwFlags);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_destroy_hash_fn)(
- HANDLE /* BCRYPT_HASH_HANDLE */ hHash);
-
-typedef NTSTATUS (WINAPI *hash_win32_cng_close_algorithm_provider_fn)(
- HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
- ULONG dwFlags);
-
-struct hash_cng_prov {
- /* DLL for CNG */
- HINSTANCE dll;
-
- /* Function pointers for CNG */
- hash_win32_cng_open_algorithm_provider_fn open_algorithm_provider;
- hash_win32_cng_get_property_fn get_property;
- hash_win32_cng_create_hash_fn create_hash;
- hash_win32_cng_finish_hash_fn finish_hash;
- hash_win32_cng_hash_data_fn hash_data;
- hash_win32_cng_destroy_hash_fn destroy_hash;
- hash_win32_cng_close_algorithm_provider_fn close_algorithm_provider;
-
- HANDLE /* BCRYPT_ALG_HANDLE */ handle;
- DWORD hash_object_size;
-};
-
-struct git_hash_prov {
- enum hash_win32_prov_type type;
-
- union {
- struct hash_cryptoapi_prov cryptoapi;
- struct hash_cng_prov cng;
- } prov;
-};
-
-/* Hash contexts */
-
-struct hash_cryptoapi_ctx {
- bool valid;
- HCRYPTHASH hash_handle;
-};
-
-struct hash_cng_ctx {
- bool updated;
- HANDLE /* BCRYPT_HASH_HANDLE */ hash_handle;
- PBYTE hash_object;
-};
-
-struct git_hash_ctx {
- enum hash_win32_prov_type type;
- git_hash_prov *prov;
-
- union {
- struct hash_cryptoapi_ctx cryptoapi;
- struct hash_cng_ctx cng;
- } ctx;
-};
-
-#endif /* INCLUDE_hash_openssl_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "git2/sys/hashsig.h"
-#include "fileops.h"
-#include "util.h"
-
-typedef uint32_t hashsig_t;
-typedef uint64_t hashsig_state;
-
-#define HASHSIG_SCALE 100
-
-#define HASHSIG_MAX_RUN 80
-#define HASHSIG_HASH_START 0x012345678ABCDEF0LL
-#define HASHSIG_HASH_SHIFT 5
-
-#define HASHSIG_HASH_MIX(S,CH) \
- (S) = ((S) << HASHSIG_HASH_SHIFT) - (S) + (hashsig_state)(CH)
-
-#define HASHSIG_HEAP_SIZE ((1 << 7) - 1)
-#define HASHSIG_HEAP_MIN_SIZE 4
-
-typedef int (*hashsig_cmp)(const void *a, const void *b, void *);
-
-typedef struct {
- int size, asize;
- hashsig_cmp cmp;
- hashsig_t values[HASHSIG_HEAP_SIZE];
-} hashsig_heap;
-
-struct git_hashsig {
- hashsig_heap mins;
- hashsig_heap maxs;
- size_t lines;
- git_hashsig_option_t opt;
-};
-
-#define HEAP_LCHILD_OF(I) (((I)<<1)+1)
-#define HEAP_RCHILD_OF(I) (((I)<<1)+2)
-#define HEAP_PARENT_OF(I) (((I)-1)>>1)
-
-static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp)
-{
- h->size = 0;
- h->asize = HASHSIG_HEAP_SIZE;
- h->cmp = cmp;
-}
-
-static int hashsig_cmp_max(const void *a, const void *b, void *payload)
-{
- hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b;
- GIT_UNUSED(payload);
- return (av < bv) ? -1 : (av > bv) ? 1 : 0;
-}
-
-static int hashsig_cmp_min(const void *a, const void *b, void *payload)
-{
- hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b;
- GIT_UNUSED(payload);
- return (av > bv) ? -1 : (av < bv) ? 1 : 0;
-}
-
-static void hashsig_heap_up(hashsig_heap *h, int el)
-{
- int parent_el = HEAP_PARENT_OF(el);
-
- while (el > 0 && h->cmp(&h->values[parent_el], &h->values[el], NULL) > 0) {
- hashsig_t t = h->values[el];
- h->values[el] = h->values[parent_el];
- h->values[parent_el] = t;
-
- el = parent_el;
- parent_el = HEAP_PARENT_OF(el);
- }
-}
-
-static void hashsig_heap_down(hashsig_heap *h, int el)
-{
- hashsig_t v, lv, rv;
-
- /* 'el < h->size / 2' tests if el is bottom row of heap */
-
- while (el < h->size / 2) {
- int lel = HEAP_LCHILD_OF(el), rel = HEAP_RCHILD_OF(el), swapel;
-
- v = h->values[el];
- lv = h->values[lel];
- rv = h->values[rel];
-
- if (h->cmp(&v, &lv, NULL) < 0 && h->cmp(&v, &rv, NULL) < 0)
- break;
-
- swapel = (h->cmp(&lv, &rv, NULL) < 0) ? lel : rel;
-
- h->values[el] = h->values[swapel];
- h->values[swapel] = v;
-
- el = swapel;
- }
-}
-
-static void hashsig_heap_sort(hashsig_heap *h)
-{
- /* only need to do this at the end for signature comparison */
- git__qsort_r(h->values, h->size, sizeof(hashsig_t), h->cmp, NULL);
-}
-
-static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val)
-{
- /* if heap is not full, insert new element */
- if (h->size < h->asize) {
- h->values[h->size++] = val;
- hashsig_heap_up(h, h->size - 1);
- }
-
- /* if heap is full, pop top if new element should replace it */
- else if (h->cmp(&val, &h->values[0], NULL) > 0) {
- h->size--;
- h->values[0] = h->values[h->size];
- hashsig_heap_down(h, 0);
- }
-
-}
-
-typedef struct {
- int use_ignores;
- uint8_t ignore_ch[256];
-} hashsig_in_progress;
-
-static void hashsig_in_progress_init(
- hashsig_in_progress *prog, git_hashsig *sig)
-{
- int i;
-
- /* no more than one can be set */
- assert(!(sig->opt & GIT_HASHSIG_IGNORE_WHITESPACE) ||
- !(sig->opt & GIT_HASHSIG_SMART_WHITESPACE));
-
- if (sig->opt & GIT_HASHSIG_IGNORE_WHITESPACE) {
- for (i = 0; i < 256; ++i)
- prog->ignore_ch[i] = git__isspace_nonlf(i);
- prog->use_ignores = 1;
- } else if (sig->opt & GIT_HASHSIG_SMART_WHITESPACE) {
- for (i = 0; i < 256; ++i)
- prog->ignore_ch[i] = git__isspace(i);
- prog->use_ignores = 1;
- } else {
- memset(prog, 0, sizeof(*prog));
- }
-}
-
-static int hashsig_add_hashes(
- git_hashsig *sig,
- const uint8_t *data,
- size_t size,
- hashsig_in_progress *prog)
-{
- const uint8_t *scan = data, *end = data + size;
- hashsig_state state = HASHSIG_HASH_START;
- int use_ignores = prog->use_ignores, len;
- uint8_t ch;
-
- while (scan < end) {
- state = HASHSIG_HASH_START;
-
- for (len = 0; scan < end && len < HASHSIG_MAX_RUN; ) {
- ch = *scan;
-
- if (use_ignores)
- for (; scan < end && git__isspace_nonlf(ch); ch = *scan)
- ++scan;
- else if (sig->opt &
- (GIT_HASHSIG_IGNORE_WHITESPACE | GIT_HASHSIG_SMART_WHITESPACE))
- for (; scan < end && ch == '\r'; ch = *scan)
- ++scan;
-
- /* peek at next character to decide what to do next */
- if (sig->opt & GIT_HASHSIG_SMART_WHITESPACE)
- use_ignores = (ch == '\n');
-
- if (scan >= end)
- break;
- ++scan;
-
- /* check run terminator */
- if (ch == '\n' || ch == '\0') {
- sig->lines++;
- break;
- }
-
- ++len;
- HASHSIG_HASH_MIX(state, ch);
- }
-
- if (len > 0) {
- hashsig_heap_insert(&sig->mins, (hashsig_t)state);
- hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
-
- while (scan < end && (*scan == '\n' || !*scan))
- ++scan;
- }
- }
-
- prog->use_ignores = use_ignores;
-
- return 0;
-}
-
-static int hashsig_finalize_hashes(git_hashsig *sig)
-{
- if (sig->mins.size < HASHSIG_HEAP_MIN_SIZE &&
- !(sig->opt & GIT_HASHSIG_ALLOW_SMALL_FILES)) {
- giterr_set(GITERR_INVALID,
- "File too small for similarity signature calculation");
- return GIT_EBUFS;
- }
-
- hashsig_heap_sort(&sig->mins);
- hashsig_heap_sort(&sig->maxs);
-
- return 0;
-}
-
-static git_hashsig *hashsig_alloc(git_hashsig_option_t opts)
-{
- git_hashsig *sig = git__calloc(1, sizeof(git_hashsig));
- if (!sig)
- return NULL;
-
- hashsig_heap_init(&sig->mins, hashsig_cmp_min);
- hashsig_heap_init(&sig->maxs, hashsig_cmp_max);
- sig->opt = opts;
-
- return sig;
-}
-
-int git_hashsig_create(
- git_hashsig **out,
- const char *buf,
- size_t buflen,
- git_hashsig_option_t opts)
-{
- int error;
- hashsig_in_progress prog;
- git_hashsig *sig = hashsig_alloc(opts);
- GITERR_CHECK_ALLOC(sig);
-
- hashsig_in_progress_init(&prog, sig);
-
- error = hashsig_add_hashes(sig, (const uint8_t *)buf, buflen, &prog);
-
- if (!error)
- error = hashsig_finalize_hashes(sig);
-
- if (!error)
- *out = sig;
- else
- git_hashsig_free(sig);
-
- return error;
-}
-
-int git_hashsig_create_fromfile(
- git_hashsig **out,
- const char *path,
- git_hashsig_option_t opts)
-{
- uint8_t buf[0x1000];
- ssize_t buflen = 0;
- int error = 0, fd;
- hashsig_in_progress prog;
- git_hashsig *sig = hashsig_alloc(opts);
- GITERR_CHECK_ALLOC(sig);
-
- if ((fd = git_futils_open_ro(path)) < 0) {
- git__free(sig);
- return fd;
- }
-
- hashsig_in_progress_init(&prog, sig);
-
- while (!error) {
- if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) {
- if ((error = (int)buflen) < 0)
- giterr_set(GITERR_OS,
- "Read error on '%s' calculating similarity hashes", path);
- break;
- }
-
- error = hashsig_add_hashes(sig, buf, buflen, &prog);
- }
-
- p_close(fd);
-
- if (!error)
- error = hashsig_finalize_hashes(sig);
-
- if (!error)
- *out = sig;
- else
- git_hashsig_free(sig);
-
- return error;
-}
-
-void git_hashsig_free(git_hashsig *sig)
-{
- git__free(sig);
-}
-
-static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b)
-{
- int matches = 0, i, j, cmp;
-
- assert(a->cmp == b->cmp);
-
- /* hash heaps are sorted - just look for overlap vs total */
-
- for (i = 0, j = 0; i < a->size && j < b->size; ) {
- cmp = a->cmp(&a->values[i], &b->values[j], NULL);
-
- if (cmp < 0)
- ++i;
- else if (cmp > 0)
- ++j;
- else {
- ++i; ++j; ++matches;
- }
- }
-
- return HASHSIG_SCALE * (matches * 2) / (a->size + b->size);
-}
-
-int git_hashsig_compare(const git_hashsig *a, const git_hashsig *b)
-{
- /* if we have no elements in either file then each file is either
- * empty or blank. if we're ignoring whitespace then the files are
- * similar, otherwise they're dissimilar.
- */
- if (a->mins.size == 0 && b->mins.size == 0) {
- if ((!a->lines && !b->lines) ||
- (a->opt & GIT_HASHSIG_IGNORE_WHITESPACE))
- return HASHSIG_SCALE;
- else
- return 0;
- }
-
- /* if we have fewer than the maximum number of elements, then just use
- * one array since the two arrays will be the same
- */
- if (a->mins.size < HASHSIG_HEAP_SIZE)
- return hashsig_heap_compare(&a->mins, &b->mins);
- else
- return (hashsig_heap_compare(&a->mins, &b->mins) +
- hashsig_heap_compare(&a->maxs, &b->maxs)) / 2;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/sys/filter.h"
-#include "filter.h"
-#include "buffer.h"
-#include "buf_text.h"
-
-static int ident_find_id(
- const char **id_start, const char **id_end, const char *start, size_t len)
-{
- const char *end = start + len, *found = NULL;
-
- while (len > 3 && (found = memchr(start, '$', len)) != NULL) {
- size_t remaining = (size_t)(end - found) - 1;
- if (remaining < 3)
- return GIT_ENOTFOUND;
-
- start = found + 1;
- len = remaining;
-
- if (start[0] == 'I' && start[1] == 'd')
- break;
- }
-
- if (len < 3 || !found)
- return GIT_ENOTFOUND;
- *id_start = found;
-
- if ((found = memchr(start + 2, '$', len - 2)) == NULL)
- return GIT_ENOTFOUND;
-
- *id_end = found + 1;
- return 0;
-}
-
-static int ident_insert_id(
- git_buf *to, const git_buf *from, const git_filter_source *src)
-{
- char oid[GIT_OID_HEXSZ+1];
- const char *id_start, *id_end, *from_end = from->ptr + from->size;
- size_t need_size;
-
- /* replace $Id$ with blob id */
-
- if (!git_filter_source_id(src))
- return GIT_PASSTHROUGH;
-
- git_oid_tostr(oid, sizeof(oid), git_filter_source_id(src));
-
- if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
- return GIT_PASSTHROUGH;
-
- need_size = (size_t)(id_start - from->ptr) +
- 5 /* "$Id: " */ + GIT_OID_HEXSZ + 2 /* " $" */ +
- (size_t)(from_end - id_end);
-
- if (git_buf_grow(to, need_size) < 0)
- return -1;
-
- git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr));
- git_buf_put(to, "$Id: ", 5);
- git_buf_put(to, oid, GIT_OID_HEXSZ);
- git_buf_put(to, " $", 2);
- git_buf_put(to, id_end, (size_t)(from_end - id_end));
-
- return git_buf_oom(to) ? -1 : 0;
-}
-
-static int ident_remove_id(
- git_buf *to, const git_buf *from)
-{
- const char *id_start, *id_end, *from_end = from->ptr + from->size;
- size_t need_size;
-
- if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
- return GIT_PASSTHROUGH;
-
- need_size = (size_t)(id_start - from->ptr) +
- 4 /* "$Id$" */ + (size_t)(from_end - id_end);
-
- if (git_buf_grow(to, need_size) < 0)
- return -1;
-
- git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr));
- git_buf_put(to, "$Id$", 4);
- git_buf_put(to, id_end, (size_t)(from_end - id_end));
-
- return git_buf_oom(to) ? -1 : 0;
-}
-
-static int ident_apply(
- git_filter *self,
- void **payload,
- git_buf *to,
- const git_buf *from,
- const git_filter_source *src)
-{
- GIT_UNUSED(self); GIT_UNUSED(payload);
-
- /* Don't filter binary files */
- if (git_buf_text_is_binary(from))
- return GIT_PASSTHROUGH;
-
- if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
- return ident_insert_id(to, from, src);
- else
- return ident_remove_id(to, from);
-}
-
-git_filter *git_ident_filter_new(void)
-{
- git_filter *f = git__calloc(1, sizeof(git_filter));
- if (f == NULL)
- return NULL;
-
- f->version = GIT_FILTER_VERSION;
- f->attributes = "+ident"; /* apply to files with ident attribute set */
- f->shutdown = git_filter_free;
- f->apply = ident_apply;
-
- return f;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_idxmap_h__
-#define INCLUDE_idxmap_h__
-
-#include <ctype.h>
-#include "common.h"
-#include "git2/index.h"
-
-#define kmalloc git__malloc
-#define kcalloc git__calloc
-#define krealloc git__realloc
-#define kreallocarray git__reallocarray
-#define kfree git__free
-#include "khash.h"
-
-__KHASH_TYPE(idx, const git_index_entry *, git_index_entry *)
-__KHASH_TYPE(idxicase, const git_index_entry *, git_index_entry *)
-
-typedef khash_t(idx) git_idxmap;
-typedef khash_t(idxicase) git_idxmap_icase;
-
-typedef khiter_t git_idxmap_iter;
-
-/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */
-static kh_inline khint_t idxentry_hash(const git_index_entry *e)
-{
- const char *s = e->path;
- khint_t h = (khint_t)git__tolower(*s);
- if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s);
- return h + GIT_IDXENTRY_STAGE(e);
-}
-
-#define idxentry_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcmp(a->path, b->path) == 0)
-#define idxentry_icase_equal(a, b) (GIT_IDXENTRY_STAGE(a) == GIT_IDXENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0)
-
-#define GIT__USE_IDXMAP \
- __KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal)
-
-#define GIT__USE_IDXMAP_ICASE \
- __KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal)
-
-#define git_idxmap_alloc(hp) \
- ((*(hp) = kh_init(idx)) == NULL) ? giterr_set_oom(), -1 : 0
-
-#define git_idxmap_icase_alloc(hp) \
- ((*(hp) = kh_init(idxicase)) == NULL) ? giterr_set_oom(), -1 : 0
-
-#define git_idxmap_insert(h, key, val, rval) do { \
- khiter_t __pos = kh_put(idx, h, key, &rval); \
- if (rval >= 0) { \
- if (rval == 0) kh_key(h, __pos) = key; \
- kh_val(h, __pos) = val; \
- } } while (0)
-
-#define git_idxmap_icase_insert(h, key, val, rval) do { \
- khiter_t __pos = kh_put(idxicase, h, key, &rval); \
- if (rval >= 0) { \
- if (rval == 0) kh_key(h, __pos) = key; \
- kh_val(h, __pos) = val; \
- } } while (0)
-
-#define git_idxmap_lookup_index(h, k) kh_get(idx, h, k)
-#define git_idxmap_icase_lookup_index(h, k) kh_get(idxicase, h, k)
-#define git_idxmap_value_at(h, idx) kh_val(h, idx)
-#define git_idxmap_valid_index(h, idx) (idx != kh_end(h))
-#define git_idxmap_has_data(h, idx) kh_exist(h, idx)
-
-#define git_idxmap_resize(h,s) kh_resize(idx, h, s)
-#define git_idxmap_free(h) kh_destroy(idx, h), h = NULL
-#define git_idxmap_clear(h) kh_clear(idx, h)
-
-#define git_idxmap_delete_at(h, id) kh_del(idx, h, id)
-#define git_idxmap_icase_delete_at(h, id) kh_del(idxicase, h, id)
-
-#define git_idxmap_delete(h, key) do { \
- khiter_t __pos = git_idxmap_lookup_index(h, key); \
- if (git_idxmap_valid_index(h, __pos)) \
- git_idxmap_delete_at(h, __pos); } while (0)
-
-#define git_idxmap_icase_delete(h, key) do { \
- khiter_t __pos = git_idxmap_icase_lookup_index(h, key); \
- if (git_idxmap_valid_index(h, __pos)) \
- git_idxmap_icase_delete_at(h, __pos); } while (0)
-
-#define git_idxmap_begin kh_begin
-#define git_idxmap_end kh_end
-
-#endif
+++ /dev/null
-#include "git2/ignore.h"
-#include "common.h"
-#include "ignore.h"
-#include "attrcache.h"
-#include "path.h"
-#include "config.h"
-#include "fnmatch.h"
-
-#define GIT_IGNORE_INTERNAL "[internal]exclude"
-
-#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
-
-/**
- * A negative ignore pattern can negate a positive one without
- * wildcards if it is a basename only and equals the basename of
- * the positive pattern. Thus
- *
- * foo/bar
- * !bar
- *
- * would result in foo/bar being unignored again while
- *
- * moo/foo/bar
- * !foo/bar
- *
- * would do nothing. The reverse also holds true: a positive
- * basename pattern can be negated by unignoring the basename in
- * subdirectories. Thus
- *
- * bar
- * !foo/bar
- *
- * would result in foo/bar being unignored again. As with the
- * first case,
- *
- * foo/bar
- * !moo/foo/bar
- *
- * would do nothing, again.
- */
-static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
-{
- git_attr_fnmatch *longer, *shorter;
- char *p;
-
- if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0
- && (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) {
-
- /* If lengths match we need to have an exact match */
- if (rule->length == neg->length) {
- return strcmp(rule->pattern, neg->pattern) == 0;
- } else if (rule->length < neg->length) {
- shorter = rule;
- longer = neg;
- } else {
- shorter = neg;
- longer = rule;
- }
-
- /* Otherwise, we need to check if the shorter
- * rule is a basename only (that is, it contains
- * no path separator) and, if so, if it
- * matches the tail of the longer rule */
- p = longer->pattern + longer->length - shorter->length;
-
- if (p[-1] != '/')
- return false;
- if (memchr(shorter->pattern, '/', shorter->length) != NULL)
- return false;
-
- return memcmp(p, shorter->pattern, shorter->length) == 0;
- }
-
- return false;
-}
-
-/**
- * A negative ignore can only unignore a file which is given explicitly before, thus
- *
- * foo
- * !foo/bar
- *
- * does not unignore 'foo/bar' as it's not in the list. However
- *
- * foo/<star>
- * !foo/bar
- *
- * does unignore 'foo/bar', as it is contained within the 'foo/<star>' rule.
- */
-static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match)
-{
- int error = 0;
- size_t i;
- git_attr_fnmatch *rule;
- char *path;
- git_buf buf = GIT_BUF_INIT;
-
- *out = 0;
-
- /* path of the file relative to the workdir, so we match the rules in subdirs */
- if (match->containing_dir) {
- git_buf_puts(&buf, match->containing_dir);
- }
- if (git_buf_puts(&buf, match->pattern) < 0)
- return -1;
-
- path = git_buf_detach(&buf);
-
- git_vector_foreach(rules, i, rule) {
- if (!(rule->flags & GIT_ATTR_FNMATCH_HASWILD)) {
- if (does_negate_pattern(rule, match)) {
- error = 0;
- *out = 1;
- goto out;
- }
- else
- continue;
- }
-
- /*
- * When dealing with a directory, we add '/<star>' so
- * p_fnmatch() honours FNM_PATHNAME. Checking for LEADINGDIR
- * alone isn't enough as that's also set for nagations, so we
- * need to check that NEGATIVE is off.
- */
- git_buf_clear(&buf);
- if (rule->containing_dir) {
- git_buf_puts(&buf, rule->containing_dir);
- }
-
- error = git_buf_puts(&buf, rule->pattern);
-
- if ((rule->flags & (GIT_ATTR_FNMATCH_LEADINGDIR | GIT_ATTR_FNMATCH_NEGATIVE)) == GIT_ATTR_FNMATCH_LEADINGDIR)
- error = git_buf_PUTS(&buf, "/*");
-
- if (error < 0)
- goto out;
-
- if ((error = p_fnmatch(git_buf_cstr(&buf), path, FNM_PATHNAME)) < 0) {
- giterr_set(GITERR_INVALID, "error matching pattern");
- goto out;
- }
-
- /* if we found a match, we want to keep this rule */
- if (error != FNM_NOMATCH) {
- *out = 1;
- error = 0;
- goto out;
- }
- }
-
- error = 0;
-
-out:
- git__free(path);
- git_buf_free(&buf);
- return error;
-}
-
-static int parse_ignore_file(
- git_repository *repo, git_attr_file *attrs, const char *data)
-{
- int error = 0;
- int ignore_case = false;
- const char *scan = data, *context = NULL;
- git_attr_fnmatch *match = NULL;
-
- if (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0)
- giterr_clear();
-
- /* if subdir file path, convert context for file paths */
- if (attrs->entry &&
- git_path_root(attrs->entry->path) < 0 &&
- !git__suffixcmp(attrs->entry->path, "/" GIT_IGNORE_FILE))
- context = attrs->entry->path;
-
- if (git_mutex_lock(&attrs->lock) < 0) {
- giterr_set(GITERR_OS, "Failed to lock ignore file");
- return -1;
- }
-
- while (!error && *scan) {
- int valid_rule = 1;
-
- if (!match && !(match = git__calloc(1, sizeof(*match)))) {
- error = -1;
- break;
- }
-
- match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
-
- if (!(error = git_attr_fnmatch__parse(
- match, &attrs->pool, context, &scan)))
- {
- match->flags |= GIT_ATTR_FNMATCH_IGNORE;
-
- if (ignore_case)
- match->flags |= GIT_ATTR_FNMATCH_ICASE;
-
- scan = git__next_line(scan);
-
- /* if a negative match doesn't actually do anything, throw it away */
- if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE)
- error = does_negate_rule(&valid_rule, &attrs->rules, match);
-
- if (!error && valid_rule)
- error = git_vector_insert(&attrs->rules, match);
- }
-
- if (error != 0 || !valid_rule) {
- match->pattern = NULL;
-
- if (error == GIT_ENOTFOUND)
- error = 0;
- } else {
- match = NULL; /* vector now "owns" the match */
- }
- }
-
- git_mutex_unlock(&attrs->lock);
- git__free(match);
-
- return error;
-}
-
-static int push_ignore_file(
- git_ignores *ignores,
- git_vector *which_list,
- const char *base,
- const char *filename)
-{
- int error = 0;
- git_attr_file *file = NULL;
-
- error = git_attr_cache__get(
- &file, ignores->repo, NULL, GIT_ATTR_FILE__FROM_FILE,
- base, filename, parse_ignore_file);
- if (error < 0)
- return error;
-
- if (file != NULL) {
- if ((error = git_vector_insert(which_list, file)) < 0)
- git_attr_file__free(file);
- }
-
- return error;
-}
-
-static int push_one_ignore(void *payload, const char *path)
-{
- git_ignores *ign = payload;
- ign->depth++;
- return push_ignore_file(ign, &ign->ign_path, path, GIT_IGNORE_FILE);
-}
-
-static int get_internal_ignores(git_attr_file **out, git_repository *repo)
-{
- int error;
-
- if ((error = git_attr_cache__init(repo)) < 0)
- return error;
-
- error = git_attr_cache__get(
- out, repo, NULL, GIT_ATTR_FILE__IN_MEMORY, NULL, GIT_IGNORE_INTERNAL, NULL);
-
- /* if internal rules list is empty, insert default rules */
- if (!error && !(*out)->rules.length)
- error = parse_ignore_file(repo, *out, GIT_IGNORE_DEFAULT_RULES);
-
- return error;
-}
-
-int git_ignore__for_path(
- git_repository *repo,
- const char *path,
- git_ignores *ignores)
-{
- int error = 0;
- const char *workdir = git_repository_workdir(repo);
-
- assert(ignores && path);
-
- memset(ignores, 0, sizeof(*ignores));
- ignores->repo = repo;
-
- /* Read the ignore_case flag */
- if ((error = git_repository__cvar(
- &ignores->ignore_case, repo, GIT_CVAR_IGNORECASE)) < 0)
- goto cleanup;
-
- if ((error = git_attr_cache__init(repo)) < 0)
- goto cleanup;
-
- /* given a unrooted path in a non-bare repo, resolve it */
- if (workdir && git_path_root(path) < 0) {
- git_buf local = GIT_BUF_INIT;
-
- if ((error = git_path_dirname_r(&local, path)) < 0 ||
- (error = git_path_resolve_relative(&local, 0)) < 0 ||
- (error = git_path_to_dir(&local)) < 0 ||
- (error = git_buf_joinpath(&ignores->dir, workdir, local.ptr)) < 0)
- {;} /* Nothing, we just want to stop on the first error */
- git_buf_free(&local);
- } else {
- error = git_buf_joinpath(&ignores->dir, path, "");
- }
- if (error < 0)
- goto cleanup;
-
- if (workdir && !git__prefixcmp(ignores->dir.ptr, workdir))
- ignores->dir_root = strlen(workdir);
-
- /* set up internals */
- if ((error = get_internal_ignores(&ignores->ign_internal, repo)) < 0)
- goto cleanup;
-
- /* load .gitignore up the path */
- if (workdir != NULL) {
- error = git_path_walk_up(
- &ignores->dir, workdir, push_one_ignore, ignores);
- if (error < 0)
- goto cleanup;
- }
-
- /* load .git/info/exclude */
- error = push_ignore_file(
- ignores, &ignores->ign_global,
- git_repository_path(repo), GIT_IGNORE_FILE_INREPO);
- if (error < 0)
- goto cleanup;
-
- /* load core.excludesfile */
- if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)
- error = push_ignore_file(
- ignores, &ignores->ign_global, NULL,
- git_repository_attr_cache(repo)->cfg_excl_file);
-
-cleanup:
- if (error < 0)
- git_ignore__free(ignores);
-
- return error;
-}
-
-int git_ignore__push_dir(git_ignores *ign, const char *dir)
-{
- if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
- return -1;
-
- ign->depth++;
-
- return push_ignore_file(
- ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
-}
-
-int git_ignore__pop_dir(git_ignores *ign)
-{
- if (ign->ign_path.length > 0) {
- git_attr_file *file = git_vector_last(&ign->ign_path);
- const char *start = file->entry->path, *end;
-
- /* - ign->dir looks something like "/home/user/a/b/" (or "a/b/c/d/")
- * - file->path looks something like "a/b/.gitignore
- *
- * We are popping the last directory off ign->dir. We also want
- * to remove the file from the vector if the popped directory
- * matches the ignore path. We need to test if the "a/b" part of
- * the file key matches the path we are about to pop.
- */
-
- if ((end = strrchr(start, '/')) != NULL) {
- size_t dirlen = (end - start) + 1;
- const char *relpath = ign->dir.ptr + ign->dir_root;
- size_t pathlen = ign->dir.size - ign->dir_root;
-
- if (pathlen == dirlen && !memcmp(relpath, start, dirlen)) {
- git_vector_pop(&ign->ign_path);
- git_attr_file__free(file);
- }
- }
- }
-
- if (--ign->depth > 0) {
- git_buf_rtruncate_at_char(&ign->dir, '/');
- git_path_to_dir(&ign->dir);
- }
-
- return 0;
-}
-
-void git_ignore__free(git_ignores *ignores)
-{
- unsigned int i;
- git_attr_file *file;
-
- git_attr_file__free(ignores->ign_internal);
-
- git_vector_foreach(&ignores->ign_path, i, file) {
- git_attr_file__free(file);
- ignores->ign_path.contents[i] = NULL;
- }
- git_vector_free(&ignores->ign_path);
-
- git_vector_foreach(&ignores->ign_global, i, file) {
- git_attr_file__free(file);
- ignores->ign_global.contents[i] = NULL;
- }
- git_vector_free(&ignores->ign_global);
-
- git_buf_free(&ignores->dir);
-}
-
-static bool ignore_lookup_in_rules(
- int *ignored, git_attr_file *file, git_attr_path *path)
-{
- size_t j;
- git_attr_fnmatch *match;
-
- git_vector_rforeach(&file->rules, j, match) {
- if (git_attr_fnmatch__match(match, path)) {
- *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0) ?
- GIT_IGNORE_TRUE : GIT_IGNORE_FALSE;
- return true;
- }
- }
-
- return false;
-}
-
-int git_ignore__lookup(
- int *out, git_ignores *ignores, const char *pathname, git_dir_flag dir_flag)
-{
- unsigned int i;
- git_attr_file *file;
- git_attr_path path;
-
- *out = GIT_IGNORE_NOTFOUND;
-
- if (git_attr_path__init(
- &path, pathname, git_repository_workdir(ignores->repo), dir_flag) < 0)
- return -1;
-
- /* first process builtins - success means path was found */
- if (ignore_lookup_in_rules(out, ignores->ign_internal, &path))
- goto cleanup;
-
- /* next process files in the path */
- git_vector_foreach(&ignores->ign_path, i, file) {
- if (ignore_lookup_in_rules(out, file, &path))
- goto cleanup;
- }
-
- /* last process global ignores */
- git_vector_foreach(&ignores->ign_global, i, file) {
- if (ignore_lookup_in_rules(out, file, &path))
- goto cleanup;
- }
-
-cleanup:
- git_attr_path__free(&path);
- return 0;
-}
-
-int git_ignore_add_rule(git_repository *repo, const char *rules)
-{
- int error;
- git_attr_file *ign_internal = NULL;
-
- if ((error = get_internal_ignores(&ign_internal, repo)) < 0)
- return error;
-
- error = parse_ignore_file(repo, ign_internal, rules);
- git_attr_file__free(ign_internal);
-
- return error;
-}
-
-int git_ignore_clear_internal_rules(git_repository *repo)
-{
- int error;
- git_attr_file *ign_internal;
-
- if ((error = get_internal_ignores(&ign_internal, repo)) < 0)
- return error;
-
- if (!(error = git_attr_file__clear_rules(ign_internal, true)))
- error = parse_ignore_file(
- repo, ign_internal, GIT_IGNORE_DEFAULT_RULES);
-
- git_attr_file__free(ign_internal);
- return error;
-}
-
-int git_ignore_path_is_ignored(
- int *ignored,
- git_repository *repo,
- const char *pathname)
-{
- int error;
- const char *workdir;
- git_attr_path path;
- git_ignores ignores;
- unsigned int i;
- git_attr_file *file;
-
- assert(ignored && pathname);
-
- workdir = repo ? git_repository_workdir(repo) : NULL;
-
- memset(&path, 0, sizeof(path));
- memset(&ignores, 0, sizeof(ignores));
-
- if ((error = git_attr_path__init(&path, pathname, workdir, GIT_DIR_FLAG_UNKNOWN)) < 0 ||
- (error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
- goto cleanup;
-
- while (1) {
- /* first process builtins - success means path was found */
- if (ignore_lookup_in_rules(ignored, ignores.ign_internal, &path))
- goto cleanup;
-
- /* next process files in the path */
- git_vector_foreach(&ignores.ign_path, i, file) {
- if (ignore_lookup_in_rules(ignored, file, &path))
- goto cleanup;
- }
-
- /* last process global ignores */
- git_vector_foreach(&ignores.ign_global, i, file) {
- if (ignore_lookup_in_rules(ignored, file, &path))
- goto cleanup;
- }
-
- /* move up one directory */
- if (path.basename == path.path)
- break;
- path.basename[-1] = '\0';
- while (path.basename > path.path && *path.basename != '/')
- path.basename--;
- if (path.basename > path.path)
- path.basename++;
- path.is_dir = 1;
-
- if ((error = git_ignore__pop_dir(&ignores)) < 0)
- break;
- }
-
- *ignored = 0;
-
-cleanup:
- git_attr_path__free(&path);
- git_ignore__free(&ignores);
- return error;
-}
-
-int git_ignore__check_pathspec_for_exact_ignores(
- git_repository *repo,
- git_vector *vspec,
- bool no_fnmatch)
-{
- int error = 0;
- size_t i;
- git_attr_fnmatch *match;
- int ignored;
- git_buf path = GIT_BUF_INIT;
- const char *wd, *filename;
- git_index *idx;
-
- if ((error = git_repository__ensure_not_bare(
- repo, "validate pathspec")) < 0 ||
- (error = git_repository_index(&idx, repo)) < 0)
- return error;
-
- wd = git_repository_workdir(repo);
-
- git_vector_foreach(vspec, i, match) {
- /* skip wildcard matches (if they are being used) */
- if ((match->flags & GIT_ATTR_FNMATCH_HASWILD) != 0 &&
- !no_fnmatch)
- continue;
-
- filename = match->pattern;
-
- /* if file is already in the index, it's fine */
- if (git_index_get_bypath(idx, filename, 0) != NULL)
- continue;
-
- if ((error = git_buf_joinpath(&path, wd, filename)) < 0)
- break;
-
- /* is there a file on disk that matches this exactly? */
- if (!git_path_isfile(path.ptr))
- continue;
-
- /* is that file ignored? */
- if ((error = git_ignore_path_is_ignored(&ignored, repo, filename)) < 0)
- break;
-
- if (ignored) {
- giterr_set(GITERR_INVALID, "pathspec contains ignored file '%s'",
- filename);
- error = GIT_EINVALIDSPEC;
- break;
- }
- }
-
- git_index_free(idx);
- git_buf_free(&path);
-
- return error;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_ignore_h__
-#define INCLUDE_ignore_h__
-
-#include "repository.h"
-#include "vector.h"
-#include "attr_file.h"
-
-#define GIT_IGNORE_FILE ".gitignore"
-#define GIT_IGNORE_FILE_INREPO "info/exclude"
-#define GIT_IGNORE_FILE_XDG "ignore"
-
-/* The git_ignores structure maintains three sets of ignores:
- * - internal ignores
- * - per directory ignores
- * - global ignores (at lower priority than the others)
- * As you traverse from one directory to another, you can push and pop
- * directories onto git_ignores list efficiently.
- */
-typedef struct {
- git_repository *repo;
- git_buf dir; /* current directory reflected in ign_path */
- git_attr_file *ign_internal;
- git_vector ign_path;
- git_vector ign_global;
- size_t dir_root; /* offset in dir to repo root */
- int ignore_case;
- int depth;
-} git_ignores;
-
-extern int git_ignore__for_path(
- git_repository *repo, const char *path, git_ignores *ign);
-
-extern int git_ignore__push_dir(git_ignores *ign, const char *dir);
-
-extern int git_ignore__pop_dir(git_ignores *ign);
-
-extern void git_ignore__free(git_ignores *ign);
-
-enum {
- GIT_IGNORE_UNCHECKED = -2,
- GIT_IGNORE_NOTFOUND = -1,
- GIT_IGNORE_FALSE = 0,
- GIT_IGNORE_TRUE = 1,
-};
-
-extern int git_ignore__lookup(int *out, git_ignores *ign, const char *path, git_dir_flag dir_flag);
-
-/* command line Git sometimes generates an error message if given a
- * pathspec that contains an exact match to an ignored file (provided
- * --force isn't also given). This makes it easy to check it that has
- * happened. Returns GIT_EINVALIDSPEC if the pathspec contains ignored
- * exact matches (that are not already present in the index).
- */
-extern int git_ignore__check_pathspec_for_exact_ignores(
- git_repository *repo, git_vector *pathspec, bool no_fnmatch);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include <stddef.h>
-
-#include "common.h"
-#include "repository.h"
-#include "index.h"
-#include "tree.h"
-#include "tree-cache.h"
-#include "hash.h"
-#include "iterator.h"
-#include "pathspec.h"
-#include "ignore.h"
-#include "blob.h"
-#include "idxmap.h"
-#include "diff.h"
-#include "varint.h"
-
-#include "git2/odb.h"
-#include "git2/oid.h"
-#include "git2/blob.h"
-#include "git2/config.h"
-#include "git2/sys/index.h"
-
-GIT__USE_IDXMAP
-GIT__USE_IDXMAP_ICASE
-
-#define INSERT_IN_MAP_EX(idx, map, e, err) do { \
- if ((idx)->ignore_case) \
- git_idxmap_icase_insert((khash_t(idxicase) *) (map), (e), (e), (err)); \
- else \
- git_idxmap_insert((map), (e), (e), (err)); \
- } while (0)
-
-#define INSERT_IN_MAP(idx, e, err) INSERT_IN_MAP_EX(idx, (idx)->entries_map, e, err)
-
-#define LOOKUP_IN_MAP(p, idx, k) do { \
- if ((idx)->ignore_case) \
- (p) = git_idxmap_icase_lookup_index((khash_t(idxicase) *) index->entries_map, (k)); \
- else \
- (p) = git_idxmap_lookup_index(index->entries_map, (k)); \
- } while (0)
-
-#define DELETE_IN_MAP(idx, e) do { \
- if ((idx)->ignore_case) \
- git_idxmap_icase_delete((khash_t(idxicase) *) (idx)->entries_map, (e)); \
- else \
- git_idxmap_delete((idx)->entries_map, (e)); \
- } while (0)
-
-static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths,
- unsigned int flags,
- git_index_matched_path_cb cb, void *payload);
-
-#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)
-#define short_entry_size(len) entry_size(struct entry_short, len)
-#define long_entry_size(len) entry_size(struct entry_long, len)
-
-#define minimal_entry_size (offsetof(struct entry_short, path))
-
-static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ;
-static const size_t INDEX_HEADER_SIZE = 12;
-
-static const unsigned int INDEX_VERSION_NUMBER_DEFAULT = 2;
-static const unsigned int INDEX_VERSION_NUMBER_LB = 2;
-static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;
-static const unsigned int INDEX_VERSION_NUMBER_COMP = 4;
-static const unsigned int INDEX_VERSION_NUMBER_UB = 4;
-
-static const unsigned int INDEX_HEADER_SIG = 0x44495243;
-static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
-static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'};
-static const char INDEX_EXT_CONFLICT_NAME_SIG[] = {'N', 'A', 'M', 'E'};
-
-#define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx)))
-
-struct index_header {
- uint32_t signature;
- uint32_t version;
- uint32_t entry_count;
-};
-
-struct index_extension {
- char signature[4];
- uint32_t extension_size;
-};
-
-struct entry_time {
- uint32_t seconds;
- uint32_t nanoseconds;
-};
-
-struct entry_short {
- struct entry_time ctime;
- struct entry_time mtime;
- uint32_t dev;
- uint32_t ino;
- uint32_t mode;
- uint32_t uid;
- uint32_t gid;
- uint32_t file_size;
- git_oid oid;
- uint16_t flags;
- char path[1]; /* arbitrary length */
-};
-
-struct entry_long {
- struct entry_time ctime;
- struct entry_time mtime;
- uint32_t dev;
- uint32_t ino;
- uint32_t mode;
- uint32_t uid;
- uint32_t gid;
- uint32_t file_size;
- git_oid oid;
- uint16_t flags;
- uint16_t flags_extended;
- char path[1]; /* arbitrary length */
-};
-
-struct entry_srch_key {
- const char *path;
- size_t pathlen;
- int stage;
-};
-
-struct entry_internal {
- git_index_entry entry;
- size_t pathlen;
- char path[GIT_FLEX_ARRAY];
-};
-
-struct reuc_entry_internal {
- git_index_reuc_entry entry;
- size_t pathlen;
- char path[GIT_FLEX_ARRAY];
-};
-
-/* local declarations */
-static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size);
-static int read_header(struct index_header *dest, const void *buffer);
-
-static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
-static bool is_index_extended(git_index *index);
-static int write_index(git_oid *checksum, git_index *index, git_filebuf *file);
-
-static void index_entry_free(git_index_entry *entry);
-static void index_entry_reuc_free(git_index_reuc_entry *reuc);
-
-int git_index_entry_srch(const void *key, const void *array_member)
-{
- const struct entry_srch_key *srch_key = key;
- const struct entry_internal *entry = array_member;
- int cmp;
- size_t len1, len2, len;
-
- len1 = srch_key->pathlen;
- len2 = entry->pathlen;
- len = len1 < len2 ? len1 : len2;
-
- cmp = memcmp(srch_key->path, entry->path, len);
- if (cmp)
- return cmp;
- if (len1 < len2)
- return -1;
- if (len1 > len2)
- return 1;
-
- if (srch_key->stage != GIT_INDEX_STAGE_ANY)
- return srch_key->stage - GIT_IDXENTRY_STAGE(&entry->entry);
-
- return 0;
-}
-
-int git_index_entry_isrch(const void *key, const void *array_member)
-{
- const struct entry_srch_key *srch_key = key;
- const struct entry_internal *entry = array_member;
- int cmp;
- size_t len1, len2, len;
-
- len1 = srch_key->pathlen;
- len2 = entry->pathlen;
- len = len1 < len2 ? len1 : len2;
-
- cmp = strncasecmp(srch_key->path, entry->path, len);
-
- if (cmp)
- return cmp;
- if (len1 < len2)
- return -1;
- if (len1 > len2)
- return 1;
-
- if (srch_key->stage != GIT_INDEX_STAGE_ANY)
- return srch_key->stage - GIT_IDXENTRY_STAGE(&entry->entry);
-
- return 0;
-}
-
-static int index_entry_srch_path(const void *path, const void *array_member)
-{
- const git_index_entry *entry = array_member;
-
- return strcmp((const char *)path, entry->path);
-}
-
-static int index_entry_isrch_path(const void *path, const void *array_member)
-{
- const git_index_entry *entry = array_member;
-
- return strcasecmp((const char *)path, entry->path);
-}
-
-int git_index_entry_cmp(const void *a, const void *b)
-{
- int diff;
- const git_index_entry *entry_a = a;
- const git_index_entry *entry_b = b;
-
- diff = strcmp(entry_a->path, entry_b->path);
-
- if (diff == 0)
- diff = (GIT_IDXENTRY_STAGE(entry_a) - GIT_IDXENTRY_STAGE(entry_b));
-
- return diff;
-}
-
-int git_index_entry_icmp(const void *a, const void *b)
-{
- int diff;
- const git_index_entry *entry_a = a;
- const git_index_entry *entry_b = b;
-
- diff = strcasecmp(entry_a->path, entry_b->path);
-
- if (diff == 0)
- diff = (GIT_IDXENTRY_STAGE(entry_a) - GIT_IDXENTRY_STAGE(entry_b));
-
- return diff;
-}
-
-static int conflict_name_cmp(const void *a, const void *b)
-{
- const git_index_name_entry *name_a = a;
- const git_index_name_entry *name_b = b;
-
- if (name_a->ancestor && !name_b->ancestor)
- return 1;
-
- if (!name_a->ancestor && name_b->ancestor)
- return -1;
-
- if (name_a->ancestor)
- return strcmp(name_a->ancestor, name_b->ancestor);
-
- if (!name_a->ours || !name_b->ours)
- return 0;
-
- return strcmp(name_a->ours, name_b->ours);
-}
-
-/**
- * TODO: enable this when resolving case insensitive conflicts
- */
-#if 0
-static int conflict_name_icmp(const void *a, const void *b)
-{
- const git_index_name_entry *name_a = a;
- const git_index_name_entry *name_b = b;
-
- if (name_a->ancestor && !name_b->ancestor)
- return 1;
-
- if (!name_a->ancestor && name_b->ancestor)
- return -1;
-
- if (name_a->ancestor)
- return strcasecmp(name_a->ancestor, name_b->ancestor);
-
- if (!name_a->ours || !name_b->ours)
- return 0;
-
- return strcasecmp(name_a->ours, name_b->ours);
-}
-#endif
-
-static int reuc_srch(const void *key, const void *array_member)
-{
- const git_index_reuc_entry *reuc = array_member;
-
- return strcmp(key, reuc->path);
-}
-
-static int reuc_isrch(const void *key, const void *array_member)
-{
- const git_index_reuc_entry *reuc = array_member;
-
- return strcasecmp(key, reuc->path);
-}
-
-static int reuc_cmp(const void *a, const void *b)
-{
- const git_index_reuc_entry *info_a = a;
- const git_index_reuc_entry *info_b = b;
-
- return strcmp(info_a->path, info_b->path);
-}
-
-static int reuc_icmp(const void *a, const void *b)
-{
- const git_index_reuc_entry *info_a = a;
- const git_index_reuc_entry *info_b = b;
-
- return strcasecmp(info_a->path, info_b->path);
-}
-
-static void index_entry_reuc_free(git_index_reuc_entry *reuc)
-{
- git__free(reuc);
-}
-
-static void index_entry_free(git_index_entry *entry)
-{
- if (!entry)
- return;
-
- memset(&entry->id, 0, sizeof(entry->id));
- git__free(entry);
-}
-
-unsigned int git_index__create_mode(unsigned int mode)
-{
- if (S_ISLNK(mode))
- return S_IFLNK;
-
- if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR))
- return (S_IFLNK | S_IFDIR);
-
- return S_IFREG | GIT_PERMS_CANONICAL(mode);
-}
-
-static unsigned int index_merge_mode(
- git_index *index, git_index_entry *existing, unsigned int mode)
-{
- if (index->no_symlinks && S_ISREG(mode) &&
- existing && S_ISLNK(existing->mode))
- return existing->mode;
-
- if (index->distrust_filemode && S_ISREG(mode))
- return (existing && S_ISREG(existing->mode)) ?
- existing->mode : git_index__create_mode(0666);
-
- return git_index__create_mode(mode);
-}
-
-GIT_INLINE(int) index_find_in_entries(
- size_t *out, git_vector *entries, git_vector_cmp entry_srch,
- const char *path, size_t path_len, int stage)
-{
- struct entry_srch_key srch_key;
- srch_key.path = path;
- srch_key.pathlen = !path_len ? strlen(path) : path_len;
- srch_key.stage = stage;
- return git_vector_bsearch2(out, entries, entry_srch, &srch_key);
-}
-
-GIT_INLINE(int) index_find(
- size_t *out, git_index *index,
- const char *path, size_t path_len, int stage)
-{
- git_vector_sort(&index->entries);
-
- return index_find_in_entries(
- out, &index->entries, index->entries_search, path, path_len, stage);
-}
-
-void git_index__set_ignore_case(git_index *index, bool ignore_case)
-{
- index->ignore_case = ignore_case;
-
- if (ignore_case) {
- index->entries_cmp_path = git__strcasecmp_cb;
- index->entries_search = git_index_entry_isrch;
- index->entries_search_path = index_entry_isrch_path;
- index->reuc_search = reuc_isrch;
- } else {
- index->entries_cmp_path = git__strcmp_cb;
- index->entries_search = git_index_entry_srch;
- index->entries_search_path = index_entry_srch_path;
- index->reuc_search = reuc_srch;
- }
-
- git_vector_set_cmp(&index->entries,
- ignore_case ? git_index_entry_icmp : git_index_entry_cmp);
- git_vector_sort(&index->entries);
-
- git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp);
- git_vector_sort(&index->reuc);
-}
-
-int git_index_open(git_index **index_out, const char *index_path)
-{
- git_index *index;
- int error = -1;
-
- assert(index_out);
-
- index = git__calloc(1, sizeof(git_index));
- GITERR_CHECK_ALLOC(index);
-
- git_pool_init(&index->tree_pool, 1);
-
- if (index_path != NULL) {
- index->index_file_path = git__strdup(index_path);
- if (!index->index_file_path)
- goto fail;
-
- /* Check if index file is stored on disk already */
- if (git_path_exists(index->index_file_path) == true)
- index->on_disk = 1;
- }
-
- if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 ||
- git_idxmap_alloc(&index->entries_map) < 0 ||
- git_vector_init(&index->names, 8, conflict_name_cmp) < 0 ||
- git_vector_init(&index->reuc, 8, reuc_cmp) < 0 ||
- git_vector_init(&index->deleted, 8, git_index_entry_cmp) < 0)
- goto fail;
-
- index->entries_cmp_path = git__strcmp_cb;
- index->entries_search = git_index_entry_srch;
- index->entries_search_path = index_entry_srch_path;
- index->reuc_search = reuc_srch;
- index->version = INDEX_VERSION_NUMBER_DEFAULT;
-
- if (index_path != NULL && (error = git_index_read(index, true)) < 0)
- goto fail;
-
- *index_out = index;
- GIT_REFCOUNT_INC(index);
-
- return 0;
-
-fail:
- git_pool_clear(&index->tree_pool);
- git_index_free(index);
- return error;
-}
-
-int git_index_new(git_index **out)
-{
- return git_index_open(out, NULL);
-}
-
-static void index_free(git_index *index)
-{
- /* index iterators increment the refcount of the index, so if we
- * get here then there should be no outstanding iterators.
- */
- assert(!git_atomic_get(&index->readers));
-
- git_index_clear(index);
- git_idxmap_free(index->entries_map);
- git_vector_free(&index->entries);
- git_vector_free(&index->names);
- git_vector_free(&index->reuc);
- git_vector_free(&index->deleted);
-
- git__free(index->index_file_path);
-
- git__memzero(index, sizeof(*index));
- git__free(index);
-}
-
-void git_index_free(git_index *index)
-{
- if (index == NULL)
- return;
-
- GIT_REFCOUNT_DEC(index, index_free);
-}
-
-/* call with locked index */
-static void index_free_deleted(git_index *index)
-{
- int readers = (int)git_atomic_get(&index->readers);
- size_t i;
-
- if (readers > 0 || !index->deleted.length)
- return;
-
- for (i = 0; i < index->deleted.length; ++i) {
- git_index_entry *ie = git__swap(index->deleted.contents[i], NULL);
- index_entry_free(ie);
- }
-
- git_vector_clear(&index->deleted);
-}
-
-/* call with locked index */
-static int index_remove_entry(git_index *index, size_t pos)
-{
- int error = 0;
- git_index_entry *entry = git_vector_get(&index->entries, pos);
-
- if (entry != NULL) {
- git_tree_cache_invalidate_path(index->tree, entry->path);
- DELETE_IN_MAP(index, entry);
- }
-
- error = git_vector_remove(&index->entries, pos);
-
- if (!error) {
- if (git_atomic_get(&index->readers) > 0) {
- error = git_vector_insert(&index->deleted, entry);
- } else {
- index_entry_free(entry);
- }
- }
-
- return error;
-}
-
-int git_index_clear(git_index *index)
-{
- int error = 0;
-
- assert(index);
-
- index->tree = NULL;
- git_pool_clear(&index->tree_pool);
-
- git_idxmap_clear(index->entries_map);
- while (!error && index->entries.length > 0)
- error = index_remove_entry(index, index->entries.length - 1);
- index_free_deleted(index);
-
- git_index_reuc_clear(index);
- git_index_name_clear(index);
-
- git_futils_filestamp_set(&index->stamp, NULL);
-
- return error;
-}
-
-static int create_index_error(int error, const char *msg)
-{
- giterr_set_str(GITERR_INDEX, msg);
- return error;
-}
-
-int git_index_set_caps(git_index *index, int caps)
-{
- unsigned int old_ignore_case;
-
- assert(index);
-
- old_ignore_case = index->ignore_case;
-
- if (caps == GIT_INDEXCAP_FROM_OWNER) {
- git_repository *repo = INDEX_OWNER(index);
- int val;
-
- if (!repo)
- return create_index_error(
- -1, "Cannot access repository to set index caps");
-
- if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORECASE))
- index->ignore_case = (val != 0);
- if (!git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE))
- index->distrust_filemode = (val == 0);
- if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS))
- index->no_symlinks = (val == 0);
- }
- else {
- index->ignore_case = ((caps & GIT_INDEXCAP_IGNORE_CASE) != 0);
- index->distrust_filemode = ((caps & GIT_INDEXCAP_NO_FILEMODE) != 0);
- index->no_symlinks = ((caps & GIT_INDEXCAP_NO_SYMLINKS) != 0);
- }
-
- if (old_ignore_case != index->ignore_case) {
- git_index__set_ignore_case(index, (bool)index->ignore_case);
- }
-
- return 0;
-}
-
-int git_index_caps(const git_index *index)
-{
- return ((index->ignore_case ? GIT_INDEXCAP_IGNORE_CASE : 0) |
- (index->distrust_filemode ? GIT_INDEXCAP_NO_FILEMODE : 0) |
- (index->no_symlinks ? GIT_INDEXCAP_NO_SYMLINKS : 0));
-}
-
-const git_oid *git_index_checksum(git_index *index)
-{
- return &index->checksum;
-}
-
-/**
- * Returns 1 for changed, 0 for not changed and <0 for errors
- */
-static int compare_checksum(git_index *index)
-{
- int fd;
- ssize_t bytes_read;
- git_oid checksum = {{ 0 }};
-
- if ((fd = p_open(index->index_file_path, O_RDONLY)) < 0)
- return fd;
-
- if (p_lseek(fd, -20, SEEK_END) < 0) {
- p_close(fd);
- giterr_set(GITERR_OS, "failed to seek to end of file");
- return -1;
- }
-
- bytes_read = p_read(fd, &checksum, GIT_OID_RAWSZ);
- p_close(fd);
-
- if (bytes_read < 0)
- return -1;
-
- return !!git_oid_cmp(&checksum, &index->checksum);
-}
-
-int git_index_read(git_index *index, int force)
-{
- int error = 0, updated;
- git_buf buffer = GIT_BUF_INIT;
- git_futils_filestamp stamp = index->stamp;
-
- if (!index->index_file_path)
- return create_index_error(-1,
- "Failed to read index: The index is in-memory only");
-
- index->on_disk = git_path_exists(index->index_file_path);
-
- if (!index->on_disk) {
- if (force)
- return git_index_clear(index);
- return 0;
- }
-
- if ((updated = git_futils_filestamp_check(&stamp, index->index_file_path) < 0) ||
- ((updated = compare_checksum(index)) < 0)) {
- giterr_set(
- GITERR_INDEX,
- "Failed to read index: '%s' no longer exists",
- index->index_file_path);
- return updated;
- }
- if (!updated && !force)
- return 0;
-
- error = git_futils_readbuffer(&buffer, index->index_file_path);
- if (error < 0)
- return error;
-
- index->tree = NULL;
- git_pool_clear(&index->tree_pool);
-
- error = git_index_clear(index);
-
- if (!error)
- error = parse_index(index, buffer.ptr, buffer.size);
-
- if (!error)
- git_futils_filestamp_set(&index->stamp, &stamp);
-
- git_buf_free(&buffer);
- return error;
-}
-
-int git_index__changed_relative_to(
- git_index *index, const git_oid *checksum)
-{
- /* attempt to update index (ignoring errors) */
- if (git_index_read(index, false) < 0)
- giterr_clear();
-
- return !!git_oid_cmp(&index->checksum, checksum);
-}
-
-static bool is_racy_entry(git_index *index, const git_index_entry *entry)
-{
- /* Git special-cases submodules in the check */
- if (S_ISGITLINK(entry->mode))
- return false;
-
- return git_index_entry_newer_than_index(entry, index);
-}
-
-/*
- * Force the next diff to take a look at those entries which have the
- * same timestamp as the current index.
- */
-static int truncate_racily_clean(git_index *index)
-{
- size_t i;
- int error;
- git_index_entry *entry;
- git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
- git_diff *diff = NULL;
- git_vector paths = GIT_VECTOR_INIT;
- git_diff_delta *delta;
-
- /* Nothing to do if there's no repo to talk about */
- if (!INDEX_OWNER(index))
- return 0;
-
- /* If there's no workdir, we can't know where to even check */
- if (!git_repository_workdir(INDEX_OWNER(index)))
- return 0;
-
- diff_opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_DISABLE_PATHSPEC_MATCH;
- git_vector_foreach(&index->entries, i, entry) {
- if ((entry->flags_extended & GIT_IDXENTRY_UPTODATE) == 0 &&
- is_racy_entry(index, entry))
- git_vector_insert(&paths, (char *)entry->path);
- }
-
- if (paths.length == 0)
- goto done;
-
- diff_opts.pathspec.count = paths.length;
- diff_opts.pathspec.strings = (char **)paths.contents;
-
- if ((error = git_diff_index_to_workdir(&diff, INDEX_OWNER(index), index, &diff_opts)) < 0)
- return error;
-
- git_vector_foreach(&diff->deltas, i, delta) {
- entry = (git_index_entry *)git_index_get_bypath(index, delta->old_file.path, 0);
-
- /* Ensure that we have a stage 0 for this file (ie, it's not a
- * conflict), otherwise smudging it is quite pointless.
- */
- if (entry)
- entry->file_size = 0;
- }
-
-done:
- git_diff_free(diff);
- git_vector_free(&paths);
- return 0;
-}
-
-unsigned git_index_version(git_index *index)
-{
- assert(index);
-
- return index->version;
-}
-
-int git_index_set_version(git_index *index, unsigned int version)
-{
- assert(index);
-
- if (version < INDEX_VERSION_NUMBER_LB ||
- version > INDEX_VERSION_NUMBER_UB) {
- giterr_set(GITERR_INDEX, "Invalid version number");
- return -1;
- }
-
- index->version = version;
-
- return 0;
-}
-
-int git_index_write(git_index *index)
-{
- git_indexwriter writer = GIT_INDEXWRITER_INIT;
- int error;
-
- truncate_racily_clean(index);
-
- if ((error = git_indexwriter_init(&writer, index)) == 0)
- error = git_indexwriter_commit(&writer);
-
- git_indexwriter_cleanup(&writer);
-
- return error;
-}
-
-const char * git_index_path(const git_index *index)
-{
- assert(index);
- return index->index_file_path;
-}
-
-int git_index_write_tree(git_oid *oid, git_index *index)
-{
- git_repository *repo;
-
- assert(oid && index);
-
- repo = INDEX_OWNER(index);
-
- if (repo == NULL)
- return create_index_error(-1, "Failed to write tree. "
- "The index file is not backed up by an existing repository");
-
- return git_tree__write_index(oid, index, repo);
-}
-
-int git_index_write_tree_to(
- git_oid *oid, git_index *index, git_repository *repo)
-{
- assert(oid && index && repo);
- return git_tree__write_index(oid, index, repo);
-}
-
-size_t git_index_entrycount(const git_index *index)
-{
- assert(index);
- return index->entries.length;
-}
-
-const git_index_entry *git_index_get_byindex(
- git_index *index, size_t n)
-{
- assert(index);
- git_vector_sort(&index->entries);
- return git_vector_get(&index->entries, n);
-}
-
-const git_index_entry *git_index_get_bypath(
- git_index *index, const char *path, int stage)
-{
- khiter_t pos;
- git_index_entry key = {{ 0 }};
-
- assert(index);
-
- key.path = path;
- GIT_IDXENTRY_STAGE_SET(&key, stage);
-
- LOOKUP_IN_MAP(pos, index, &key);
-
- if (git_idxmap_valid_index(index->entries_map, pos))
- return git_idxmap_value_at(index->entries_map, pos);
-
- giterr_set(GITERR_INDEX, "Index does not contain %s", path);
- return NULL;
-}
-
-void git_index_entry__init_from_stat(
- git_index_entry *entry, struct stat *st, bool trust_mode)
-{
- entry->ctime.seconds = (int32_t)st->st_ctime;
- entry->mtime.seconds = (int32_t)st->st_mtime;
-#if defined(GIT_USE_NSEC)
- entry->mtime.nanoseconds = st->st_mtime_nsec;
- entry->ctime.nanoseconds = st->st_ctime_nsec;
-#endif
- entry->dev = st->st_rdev;
- entry->ino = st->st_ino;
- entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ?
- git_index__create_mode(0666) : git_index__create_mode(st->st_mode);
- entry->uid = st->st_uid;
- entry->gid = st->st_gid;
- entry->file_size = (uint32_t)st->st_size;
-}
-
-static void index_entry_adjust_namemask(
- git_index_entry *entry,
- size_t path_length)
-{
- entry->flags &= ~GIT_IDXENTRY_NAMEMASK;
-
- if (path_length < GIT_IDXENTRY_NAMEMASK)
- entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK;
- else
- entry->flags |= GIT_IDXENTRY_NAMEMASK;
-}
-
-/* When `from_workdir` is true, we will validate the paths to avoid placing
- * paths that are invalid for the working directory on the current filesystem
- * (eg, on Windows, we will disallow `GIT~1`, `AUX`, `COM1`, etc). This
- * function will *always* prevent `.git` and directory traversal `../` from
- * being added to the index.
- */
-static int index_entry_create(
- git_index_entry **out,
- git_repository *repo,
- const char *path,
- bool from_workdir)
-{
- size_t pathlen = strlen(path), alloclen;
- struct entry_internal *entry;
- unsigned int path_valid_flags = GIT_PATH_REJECT_INDEX_DEFAULTS;
-
- /* always reject placing `.git` in the index and directory traversal.
- * when requested, disallow platform-specific filenames and upgrade to
- * the platform-specific `.git` tests (eg, `git~1`, etc).
- */
- if (from_workdir)
- path_valid_flags |= GIT_PATH_REJECT_WORKDIR_DEFAULTS;
-
- if (!git_path_isvalid(repo, path, path_valid_flags)) {
- giterr_set(GITERR_INDEX, "invalid path: '%s'", path);
- return -1;
- }
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(struct entry_internal), pathlen);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
- entry = git__calloc(1, alloclen);
- GITERR_CHECK_ALLOC(entry);
-
- entry->pathlen = pathlen;
- memcpy(entry->path, path, pathlen);
- entry->entry.path = entry->path;
-
- *out = (git_index_entry *)entry;
- return 0;
-}
-
-static int index_entry_init(
- git_index_entry **entry_out,
- git_index *index,
- const char *rel_path)
-{
- int error = 0;
- git_index_entry *entry = NULL;
- struct stat st;
- git_oid oid;
-
- if (INDEX_OWNER(index) == NULL)
- return create_index_error(-1,
- "Could not initialize index entry. "
- "Index is not backed up by an existing repository.");
-
- if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, true) < 0)
- return -1;
-
- /* write the blob to disk and get the oid and stat info */
- error = git_blob__create_from_paths(
- &oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true);
-
- if (error < 0) {
- index_entry_free(entry);
- return error;
- }
-
- entry->id = oid;
- git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
-
- *entry_out = (git_index_entry *)entry;
- return 0;
-}
-
-static git_index_reuc_entry *reuc_entry_alloc(const char *path)
-{
- size_t pathlen = strlen(path),
- structlen = sizeof(struct reuc_entry_internal),
- alloclen;
- struct reuc_entry_internal *entry;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclen, structlen, pathlen) ||
- GIT_ADD_SIZET_OVERFLOW(&alloclen, alloclen, 1))
- return NULL;
-
- entry = git__calloc(1, alloclen);
- if (!entry)
- return NULL;
-
- entry->pathlen = pathlen;
- memcpy(entry->path, path, pathlen);
- entry->entry.path = entry->path;
-
- return (git_index_reuc_entry *)entry;
-}
-
-static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
- const char *path,
- int ancestor_mode, const git_oid *ancestor_oid,
- int our_mode, const git_oid *our_oid,
- int their_mode, const git_oid *their_oid)
-{
- git_index_reuc_entry *reuc = NULL;
-
- assert(reuc_out && path);
-
- *reuc_out = reuc = reuc_entry_alloc(path);
- GITERR_CHECK_ALLOC(reuc);
-
- if ((reuc->mode[0] = ancestor_mode) > 0) {
- assert(ancestor_oid);
- git_oid_cpy(&reuc->oid[0], ancestor_oid);
- }
-
- if ((reuc->mode[1] = our_mode) > 0) {
- assert(our_oid);
- git_oid_cpy(&reuc->oid[1], our_oid);
- }
-
- if ((reuc->mode[2] = their_mode) > 0) {
- assert(their_oid);
- git_oid_cpy(&reuc->oid[2], their_oid);
- }
-
- return 0;
-}
-
-static void index_entry_cpy(
- git_index_entry *tgt,
- const git_index_entry *src)
-{
- const char *tgt_path = tgt->path;
- memcpy(tgt, src, sizeof(*tgt));
- tgt->path = tgt_path;
-}
-
-static int index_entry_dup(
- git_index_entry **out,
- git_index *index,
- const git_index_entry *src)
-{
- if (index_entry_create(out, INDEX_OWNER(index), src->path, false) < 0)
- return -1;
-
- index_entry_cpy(*out, src);
- return 0;
-}
-
-static void index_entry_cpy_nocache(
- git_index_entry *tgt,
- const git_index_entry *src)
-{
- git_oid_cpy(&tgt->id, &src->id);
- tgt->mode = src->mode;
- tgt->flags = src->flags;
- tgt->flags_extended = (src->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS);
-}
-
-static int index_entry_dup_nocache(
- git_index_entry **out,
- git_index *index,
- const git_index_entry *src)
-{
- if (index_entry_create(out, INDEX_OWNER(index), src->path, false) < 0)
- return -1;
-
- index_entry_cpy_nocache(*out, src);
- return 0;
-}
-
-static int has_file_name(git_index *index,
- const git_index_entry *entry, size_t pos, int ok_to_replace)
-{
- int retval = 0;
- size_t len = strlen(entry->path);
- int stage = GIT_IDXENTRY_STAGE(entry);
- const char *name = entry->path;
-
- while (pos < index->entries.length) {
- struct entry_internal *p = index->entries.contents[pos++];
-
- if (len >= p->pathlen)
- break;
- if (memcmp(name, p->path, len))
- break;
- if (GIT_IDXENTRY_STAGE(&p->entry) != stage)
- continue;
- if (p->path[len] != '/')
- continue;
- retval = -1;
- if (!ok_to_replace)
- break;
-
- if (index_remove_entry(index, --pos) < 0)
- break;
- }
- return retval;
-}
-
-/*
- * Do we have another file with a pathname that is a proper
- * subset of the name we're trying to add?
- */
-static int has_dir_name(git_index *index,
- const git_index_entry *entry, int ok_to_replace)
-{
- int retval = 0;
- int stage = GIT_IDXENTRY_STAGE(entry);
- const char *name = entry->path;
- const char *slash = name + strlen(name);
-
- for (;;) {
- size_t len, pos;
-
- for (;;) {
- if (*--slash == '/')
- break;
- if (slash <= entry->path)
- return retval;
- }
- len = slash - name;
-
- if (!index_find(&pos, index, name, len, stage)) {
- retval = -1;
- if (!ok_to_replace)
- break;
-
- if (index_remove_entry(index, pos) < 0)
- break;
- continue;
- }
-
- /*
- * Trivial optimization: if we find an entry that
- * already matches the sub-directory, then we know
- * we're ok, and we can exit.
- */
- for (; pos < index->entries.length; ++pos) {
- struct entry_internal *p = index->entries.contents[pos];
-
- if (p->pathlen <= len ||
- p->path[len] != '/' ||
- memcmp(p->path, name, len))
- break; /* not our subdirectory */
-
- if (GIT_IDXENTRY_STAGE(&p->entry) == stage)
- return retval;
- }
- }
-
- return retval;
-}
-
-static int check_file_directory_collision(git_index *index,
- git_index_entry *entry, size_t pos, int ok_to_replace)
-{
- int retval = has_file_name(index, entry, pos, ok_to_replace);
- retval = retval + has_dir_name(index, entry, ok_to_replace);
-
- if (retval) {
- giterr_set(GITERR_INDEX,
- "'%s' appears as both a file and a directory", entry->path);
- return -1;
- }
-
- return 0;
-}
-
-static int canonicalize_directory_path(
- git_index *index,
- git_index_entry *entry,
- git_index_entry *existing)
-{
- const git_index_entry *match, *best = NULL;
- char *search, *sep;
- size_t pos, search_len, best_len;
-
- if (!index->ignore_case)
- return 0;
-
- /* item already exists in the index, simply re-use the existing case */
- if (existing) {
- memcpy((char *)entry->path, existing->path, strlen(existing->path));
- return 0;
- }
-
- /* nothing to do */
- if (strchr(entry->path, '/') == NULL)
- return 0;
-
- if ((search = git__strdup(entry->path)) == NULL)
- return -1;
-
- /* starting at the parent directory and descending to the root, find the
- * common parent directory.
- */
- while (!best && (sep = strrchr(search, '/'))) {
- sep[1] = '\0';
-
- search_len = strlen(search);
-
- git_vector_bsearch2(
- &pos, &index->entries, index->entries_search_path, search);
-
- while ((match = git_vector_get(&index->entries, pos))) {
- if (GIT_IDXENTRY_STAGE(match) != 0) {
- /* conflicts do not contribute to canonical paths */
- } else if (strncmp(search, match->path, search_len) == 0) {
- /* prefer an exact match to the input filename */
- best = match;
- best_len = search_len;
- break;
- } else if (strncasecmp(search, match->path, search_len) == 0) {
- /* continue walking, there may be a path with an exact
- * (case sensitive) match later in the index, but use this
- * as the best match until that happens.
- */
- if (!best) {
- best = match;
- best_len = search_len;
- }
- } else {
- break;
- }
-
- pos++;
- }
-
- sep[0] = '\0';
- }
-
- if (best)
- memcpy((char *)entry->path, best->path, best_len);
-
- git__free(search);
- return 0;
-}
-
-static int index_no_dups(void **old, void *new)
-{
- const git_index_entry *entry = new;
- GIT_UNUSED(old);
- giterr_set(GITERR_INDEX, "'%s' appears multiple times at stage %d",
- entry->path, GIT_IDXENTRY_STAGE(entry));
- return GIT_EEXISTS;
-}
-
-static void index_existing_and_best(
- git_index_entry **existing,
- size_t *existing_position,
- git_index_entry **best,
- git_index *index,
- const git_index_entry *entry)
-{
- git_index_entry *e;
- size_t pos;
- int error;
-
- error = index_find(&pos,
- index, entry->path, 0, GIT_IDXENTRY_STAGE(entry));
-
- if (error == 0) {
- *existing = index->entries.contents[pos];
- *existing_position = pos;
- *best = index->entries.contents[pos];
- return;
- }
-
- *existing = NULL;
- *existing_position = 0;
- *best = NULL;
-
- if (GIT_IDXENTRY_STAGE(entry) == 0) {
- for (; pos < index->entries.length; pos++) {
- int (*strcomp)(const char *a, const char *b) =
- index->ignore_case ? git__strcasecmp : git__strcmp;
-
- e = index->entries.contents[pos];
-
- if (strcomp(entry->path, e->path) != 0)
- break;
-
- if (GIT_IDXENTRY_STAGE(e) == GIT_INDEX_STAGE_ANCESTOR) {
- *best = e;
- continue;
- } else {
- *best = e;
- break;
- }
- }
- }
-}
-
-/* index_insert takes ownership of the new entry - if it can't insert
- * it, then it will return an error **and also free the entry**. When
- * it replaces an existing entry, it will update the entry_ptr with the
- * actual entry in the index (and free the passed in one).
- *
- * trust_path is whether we use the given path, or whether (on case
- * insensitive systems only) we try to canonicalize the given path to
- * be within an existing directory.
- *
- * trust_mode is whether we trust the mode in entry_ptr.
- *
- * trust_id is whether we trust the id or it should be validated.
- */
-static int index_insert(
- git_index *index,
- git_index_entry **entry_ptr,
- int replace,
- bool trust_path,
- bool trust_mode,
- bool trust_id)
-{
- int error = 0;
- size_t path_length, position;
- git_index_entry *existing, *best, *entry;
-
- assert(index && entry_ptr);
-
- entry = *entry_ptr;
-
- /* make sure that the path length flag is correct */
- path_length = ((struct entry_internal *)entry)->pathlen;
- index_entry_adjust_namemask(entry, path_length);
-
- /* this entry is now up-to-date and should not be checked for raciness */
- entry->flags_extended |= GIT_IDXENTRY_UPTODATE;
-
- git_vector_sort(&index->entries);
-
- /* look if an entry with this path already exists, either staged, or (if
- * this entry is a regular staged item) as the "ours" side of a conflict.
- */
- index_existing_and_best(&existing, &position, &best, index, entry);
-
- /* update the file mode */
- entry->mode = trust_mode ?
- git_index__create_mode(entry->mode) :
- index_merge_mode(index, best, entry->mode);
-
- /* canonicalize the directory name */
- if (!trust_path)
- error = canonicalize_directory_path(index, entry, best);
-
- /* ensure that the given id exists (unless it's a submodule) */
- if (!error && !trust_id && INDEX_OWNER(index) &&
- (entry->mode & GIT_FILEMODE_COMMIT) != GIT_FILEMODE_COMMIT) {
-
- if (!git_object__is_valid(INDEX_OWNER(index), &entry->id,
- git_object__type_from_filemode(entry->mode)))
- error = -1;
- }
-
- /* look for tree / blob name collisions, removing conflicts if requested */
- if (!error)
- error = check_file_directory_collision(index, entry, position, replace);
-
- if (error < 0)
- /* skip changes */;
-
- /* if we are replacing an existing item, overwrite the existing entry
- * and return it in place of the passed in one.
- */
- else if (existing) {
- if (replace) {
- index_entry_cpy(existing, entry);
-
- if (trust_path)
- memcpy((char *)existing->path, entry->path, strlen(entry->path));
- }
-
- index_entry_free(entry);
- *entry_ptr = entry = existing;
- }
- else {
- /* if replace is not requested or no existing entry exists, insert
- * at the sorted position. (Since we re-sort after each insert to
- * check for dups, this is actually cheaper in the long run.)
- */
- error = git_vector_insert_sorted(&index->entries, entry, index_no_dups);
-
- if (error == 0) {
- INSERT_IN_MAP(index, entry, error);
- }
- }
-
- if (error < 0) {
- index_entry_free(*entry_ptr);
- *entry_ptr = NULL;
- }
-
- return error;
-}
-
-static int index_conflict_to_reuc(git_index *index, const char *path)
-{
- const git_index_entry *conflict_entries[3];
- int ancestor_mode, our_mode, their_mode;
- git_oid const *ancestor_oid, *our_oid, *their_oid;
- int ret;
-
- if ((ret = git_index_conflict_get(&conflict_entries[0],
- &conflict_entries[1], &conflict_entries[2], index, path)) < 0)
- return ret;
-
- ancestor_mode = conflict_entries[0] == NULL ? 0 : conflict_entries[0]->mode;
- our_mode = conflict_entries[1] == NULL ? 0 : conflict_entries[1]->mode;
- their_mode = conflict_entries[2] == NULL ? 0 : conflict_entries[2]->mode;
-
- ancestor_oid = conflict_entries[0] == NULL ? NULL : &conflict_entries[0]->id;
- our_oid = conflict_entries[1] == NULL ? NULL : &conflict_entries[1]->id;
- their_oid = conflict_entries[2] == NULL ? NULL : &conflict_entries[2]->id;
-
- if ((ret = git_index_reuc_add(index, path, ancestor_mode, ancestor_oid,
- our_mode, our_oid, their_mode, their_oid)) >= 0)
- ret = git_index_conflict_remove(index, path);
-
- return ret;
-}
-
-static bool valid_filemode(const int filemode)
-{
- return (filemode == GIT_FILEMODE_BLOB ||
- filemode == GIT_FILEMODE_BLOB_EXECUTABLE ||
- filemode == GIT_FILEMODE_LINK ||
- filemode == GIT_FILEMODE_COMMIT);
-}
-
-int git_index_add_frombuffer(
- git_index *index, const git_index_entry *source_entry,
- const void *buffer, size_t len)
-{
- git_index_entry *entry = NULL;
- int error = 0;
- git_oid id;
-
- assert(index && source_entry->path);
-
- if (INDEX_OWNER(index) == NULL)
- return create_index_error(-1,
- "Could not initialize index entry. "
- "Index is not backed up by an existing repository.");
-
- if (!valid_filemode(source_entry->mode)) {
- giterr_set(GITERR_INDEX, "invalid filemode");
- return -1;
- }
-
- if (index_entry_dup(&entry, index, source_entry) < 0)
- return -1;
-
- error = git_blob_create_frombuffer(&id, INDEX_OWNER(index), buffer, len);
- if (error < 0) {
- index_entry_free(entry);
- return error;
- }
-
- git_oid_cpy(&entry->id, &id);
- entry->file_size = len;
-
- if ((error = index_insert(index, &entry, 1, true, true, true)) < 0)
- return error;
-
- /* Adding implies conflict was resolved, move conflict entries to REUC */
- if ((error = index_conflict_to_reuc(index, entry->path)) < 0 && error != GIT_ENOTFOUND)
- return error;
-
- git_tree_cache_invalidate_path(index->tree, entry->path);
- return 0;
-}
-
-static int add_repo_as_submodule(git_index_entry **out, git_index *index, const char *path)
-{
- git_repository *sub;
- git_buf abspath = GIT_BUF_INIT;
- git_repository *repo = INDEX_OWNER(index);
- git_reference *head;
- git_index_entry *entry;
- struct stat st;
- int error;
-
- if (index_entry_create(&entry, INDEX_OWNER(index), path, true) < 0)
- return -1;
-
- if ((error = git_buf_joinpath(&abspath, git_repository_workdir(repo), path)) < 0)
- return error;
-
- if ((error = p_stat(abspath.ptr, &st)) < 0) {
- giterr_set(GITERR_OS, "failed to stat repository dir");
- return -1;
- }
-
- git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
-
- if ((error = git_repository_open(&sub, abspath.ptr)) < 0)
- return error;
-
- if ((error = git_repository_head(&head, sub)) < 0)
- return error;
-
- git_oid_cpy(&entry->id, git_reference_target(head));
- entry->mode = GIT_FILEMODE_COMMIT;
-
- git_reference_free(head);
- git_repository_free(sub);
- git_buf_free(&abspath);
-
- *out = entry;
- return 0;
-}
-
-int git_index_add_bypath(git_index *index, const char *path)
-{
- git_index_entry *entry = NULL;
- int ret;
-
- assert(index && path);
-
- if ((ret = index_entry_init(&entry, index, path)) == 0)
- ret = index_insert(index, &entry, 1, false, false, true);
-
- /* If we were given a directory, let's see if it's a submodule */
- if (ret < 0 && ret != GIT_EDIRECTORY)
- return ret;
-
- if (ret == GIT_EDIRECTORY) {
- git_submodule *sm;
- git_error_state err;
-
- giterr_state_capture(&err, ret);
-
- ret = git_submodule_lookup(&sm, INDEX_OWNER(index), path);
- if (ret == GIT_ENOTFOUND)
- return giterr_state_restore(&err);
-
- giterr_state_free(&err);
-
- /*
- * EEXISTS means that there is a repository at that path, but it's not known
- * as a submodule. We add its HEAD as an entry and don't register it.
- */
- if (ret == GIT_EEXISTS) {
- if ((ret = add_repo_as_submodule(&entry, index, path)) < 0)
- return ret;
-
- if ((ret = index_insert(index, &entry, 1, false, false, true)) < 0)
- return ret;
- } else if (ret < 0) {
- return ret;
- } else {
- ret = git_submodule_add_to_index(sm, false);
- git_submodule_free(sm);
- return ret;
- }
- }
-
- /* Adding implies conflict was resolved, move conflict entries to REUC */
- if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND)
- return ret;
-
- git_tree_cache_invalidate_path(index->tree, entry->path);
- return 0;
-}
-
-int git_index_remove_bypath(git_index *index, const char *path)
-{
- int ret;
-
- assert(index && path);
-
- if (((ret = git_index_remove(index, path, 0)) < 0 &&
- ret != GIT_ENOTFOUND) ||
- ((ret = index_conflict_to_reuc(index, path)) < 0 &&
- ret != GIT_ENOTFOUND))
- return ret;
-
- if (ret == GIT_ENOTFOUND)
- giterr_clear();
-
- return 0;
-}
-
-int git_index__fill(git_index *index, const git_vector *source_entries)
-{
- const git_index_entry *source_entry = NULL;
- size_t i;
- int ret = 0;
-
- assert(index);
-
- if (!source_entries->length)
- return 0;
-
- git_vector_size_hint(&index->entries, source_entries->length);
- git_idxmap_resize(index->entries_map, (khint_t)(source_entries->length * 1.3));
-
- git_vector_foreach(source_entries, i, source_entry) {
- git_index_entry *entry = NULL;
-
- if ((ret = index_entry_dup(&entry, index, source_entry)) < 0)
- break;
-
- index_entry_adjust_namemask(entry, ((struct entry_internal *)entry)->pathlen);
- entry->flags_extended |= GIT_IDXENTRY_UPTODATE;
- entry->mode = git_index__create_mode(entry->mode);
-
- if ((ret = git_vector_insert(&index->entries, entry)) < 0)
- break;
-
- INSERT_IN_MAP(index, entry, ret);
- if (ret < 0)
- break;
- }
-
- if (!ret)
- git_vector_sort(&index->entries);
-
- return ret;
-}
-
-
-int git_index_add(git_index *index, const git_index_entry *source_entry)
-{
- git_index_entry *entry = NULL;
- int ret;
-
- assert(index && source_entry && source_entry->path);
-
- if (!valid_filemode(source_entry->mode)) {
- giterr_set(GITERR_INDEX, "invalid filemode");
- return -1;
- }
-
- if ((ret = index_entry_dup(&entry, index, source_entry)) < 0 ||
- (ret = index_insert(index, &entry, 1, true, true, false)) < 0)
- return ret;
-
- git_tree_cache_invalidate_path(index->tree, entry->path);
- return 0;
-}
-
-int git_index_remove(git_index *index, const char *path, int stage)
-{
- int error;
- size_t position;
- git_index_entry remove_key = {{ 0 }};
-
- remove_key.path = path;
- GIT_IDXENTRY_STAGE_SET(&remove_key, stage);
-
- DELETE_IN_MAP(index, &remove_key);
-
- if (index_find(&position, index, path, 0, stage) < 0) {
- giterr_set(
- GITERR_INDEX, "Index does not contain %s at stage %d", path, stage);
- error = GIT_ENOTFOUND;
- } else {
- error = index_remove_entry(index, position);
- }
-
- return error;
-}
-
-int git_index_remove_directory(git_index *index, const char *dir, int stage)
-{
- git_buf pfx = GIT_BUF_INIT;
- int error = 0;
- size_t pos;
- git_index_entry *entry;
-
- if (!(error = git_buf_sets(&pfx, dir)) &&
- !(error = git_path_to_dir(&pfx)))
- index_find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY);
-
- while (!error) {
- entry = git_vector_get(&index->entries, pos);
- if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0)
- break;
-
- if (GIT_IDXENTRY_STAGE(entry) != stage) {
- ++pos;
- continue;
- }
-
- error = index_remove_entry(index, pos);
-
- /* removed entry at 'pos' so we don't need to increment */
- }
-
- git_buf_free(&pfx);
-
- return error;
-}
-
-int git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix)
-{
- int error = 0;
- size_t pos;
- const git_index_entry *entry;
-
- index_find(&pos, index, prefix, strlen(prefix), GIT_INDEX_STAGE_ANY);
- entry = git_vector_get(&index->entries, pos);
- if (!entry || git__prefixcmp(entry->path, prefix) != 0)
- error = GIT_ENOTFOUND;
-
- if (!error && at_pos)
- *at_pos = pos;
-
- return error;
-}
-
-int git_index__find_pos(
- size_t *out, git_index *index, const char *path, size_t path_len, int stage)
-{
- assert(index && path);
- return index_find(out, index, path, path_len, stage);
-}
-
-int git_index_find(size_t *at_pos, git_index *index, const char *path)
-{
- size_t pos;
-
- assert(index && path);
-
- if (git_vector_bsearch2(
- &pos, &index->entries, index->entries_search_path, path) < 0) {
- giterr_set(GITERR_INDEX, "Index does not contain %s", path);
- return GIT_ENOTFOUND;
- }
-
- /* Since our binary search only looked at path, we may be in the
- * middle of a list of stages.
- */
- for (; pos > 0; --pos) {
- const git_index_entry *prev = git_vector_get(&index->entries, pos - 1);
-
- if (index->entries_cmp_path(prev->path, path) != 0)
- break;
- }
-
- if (at_pos)
- *at_pos = pos;
-
- return 0;
-}
-
-int git_index_conflict_add(git_index *index,
- const git_index_entry *ancestor_entry,
- const git_index_entry *our_entry,
- const git_index_entry *their_entry)
-{
- git_index_entry *entries[3] = { 0 };
- unsigned short i;
- int ret = 0;
-
- assert (index);
-
- if ((ancestor_entry &&
- (ret = index_entry_dup(&entries[0], index, ancestor_entry)) < 0) ||
- (our_entry &&
- (ret = index_entry_dup(&entries[1], index, our_entry)) < 0) ||
- (their_entry &&
- (ret = index_entry_dup(&entries[2], index, their_entry)) < 0))
- goto on_error;
-
- /* Validate entries */
- for (i = 0; i < 3; i++) {
- if (entries[i] && !valid_filemode(entries[i]->mode)) {
- giterr_set(GITERR_INDEX, "invalid filemode for stage %d entry",
- i + 1);
- return -1;
- }
- }
-
- /* Remove existing index entries for each path */
- for (i = 0; i < 3; i++) {
- if (entries[i] == NULL)
- continue;
-
- if ((ret = git_index_remove(index, entries[i]->path, 0)) != 0) {
- if (ret != GIT_ENOTFOUND)
- goto on_error;
-
- giterr_clear();
- ret = 0;
- }
- }
-
- /* Add the conflict entries */
- for (i = 0; i < 3; i++) {
- if (entries[i] == NULL)
- continue;
-
- /* Make sure stage is correct */
- GIT_IDXENTRY_STAGE_SET(entries[i], i + 1);
-
- if ((ret = index_insert(index, &entries[i], 1, true, true, false)) < 0)
- goto on_error;
-
- entries[i] = NULL; /* don't free if later entry fails */
- }
-
- return 0;
-
-on_error:
- for (i = 0; i < 3; i++) {
- if (entries[i] != NULL)
- index_entry_free(entries[i]);
- }
-
- return ret;
-}
-
-static int index_conflict__get_byindex(
- const git_index_entry **ancestor_out,
- const git_index_entry **our_out,
- const git_index_entry **their_out,
- git_index *index,
- size_t n)
-{
- const git_index_entry *conflict_entry;
- const char *path = NULL;
- size_t count;
- int stage, len = 0;
-
- assert(ancestor_out && our_out && their_out && index);
-
- *ancestor_out = NULL;
- *our_out = NULL;
- *their_out = NULL;
-
- for (count = git_index_entrycount(index); n < count; ++n) {
- conflict_entry = git_vector_get(&index->entries, n);
-
- if (path && index->entries_cmp_path(conflict_entry->path, path) != 0)
- break;
-
- stage = GIT_IDXENTRY_STAGE(conflict_entry);
- path = conflict_entry->path;
-
- switch (stage) {
- case 3:
- *their_out = conflict_entry;
- len++;
- break;
- case 2:
- *our_out = conflict_entry;
- len++;
- break;
- case 1:
- *ancestor_out = conflict_entry;
- len++;
- break;
- default:
- break;
- };
- }
-
- return len;
-}
-
-int git_index_conflict_get(
- const git_index_entry **ancestor_out,
- const git_index_entry **our_out,
- const git_index_entry **their_out,
- git_index *index,
- const char *path)
-{
- size_t pos;
- int len = 0;
-
- assert(ancestor_out && our_out && their_out && index && path);
-
- *ancestor_out = NULL;
- *our_out = NULL;
- *their_out = NULL;
-
- if (git_index_find(&pos, index, path) < 0)
- return GIT_ENOTFOUND;
-
- if ((len = index_conflict__get_byindex(
- ancestor_out, our_out, their_out, index, pos)) < 0)
- return len;
- else if (len == 0)
- return GIT_ENOTFOUND;
-
- return 0;
-}
-
-static int index_conflict_remove(git_index *index, const char *path)
-{
- size_t pos = 0;
- git_index_entry *conflict_entry;
- int error = 0;
-
- if (path != NULL && git_index_find(&pos, index, path) < 0)
- return GIT_ENOTFOUND;
-
- while ((conflict_entry = git_vector_get(&index->entries, pos)) != NULL) {
-
- if (path != NULL &&
- index->entries_cmp_path(conflict_entry->path, path) != 0)
- break;
-
- if (GIT_IDXENTRY_STAGE(conflict_entry) == 0) {
- pos++;
- continue;
- }
-
- if ((error = index_remove_entry(index, pos)) < 0)
- break;
- }
-
- return error;
-}
-
-int git_index_conflict_remove(git_index *index, const char *path)
-{
- assert(index && path);
- return index_conflict_remove(index, path);
-}
-
-int git_index_conflict_cleanup(git_index *index)
-{
- assert(index);
- return index_conflict_remove(index, NULL);
-}
-
-int git_index_has_conflicts(const git_index *index)
-{
- size_t i;
- git_index_entry *entry;
-
- assert(index);
-
- git_vector_foreach(&index->entries, i, entry) {
- if (GIT_IDXENTRY_STAGE(entry) > 0)
- return 1;
- }
-
- return 0;
-}
-
-int git_index_conflict_iterator_new(
- git_index_conflict_iterator **iterator_out,
- git_index *index)
-{
- git_index_conflict_iterator *it = NULL;
-
- assert(iterator_out && index);
-
- it = git__calloc(1, sizeof(git_index_conflict_iterator));
- GITERR_CHECK_ALLOC(it);
-
- it->index = index;
-
- *iterator_out = it;
- return 0;
-}
-
-int git_index_conflict_next(
- const git_index_entry **ancestor_out,
- const git_index_entry **our_out,
- const git_index_entry **their_out,
- git_index_conflict_iterator *iterator)
-{
- const git_index_entry *entry;
- int len;
-
- assert(ancestor_out && our_out && their_out && iterator);
-
- *ancestor_out = NULL;
- *our_out = NULL;
- *their_out = NULL;
-
- while (iterator->cur < iterator->index->entries.length) {
- entry = git_index_get_byindex(iterator->index, iterator->cur);
-
- if (git_index_entry_is_conflict(entry)) {
- if ((len = index_conflict__get_byindex(
- ancestor_out,
- our_out,
- their_out,
- iterator->index,
- iterator->cur)) < 0)
- return len;
-
- iterator->cur += len;
- return 0;
- }
-
- iterator->cur++;
- }
-
- return GIT_ITEROVER;
-}
-
-void git_index_conflict_iterator_free(git_index_conflict_iterator *iterator)
-{
- if (iterator == NULL)
- return;
-
- git__free(iterator);
-}
-
-size_t git_index_name_entrycount(git_index *index)
-{
- assert(index);
- return index->names.length;
-}
-
-const git_index_name_entry *git_index_name_get_byindex(
- git_index *index, size_t n)
-{
- assert(index);
-
- git_vector_sort(&index->names);
- return git_vector_get(&index->names, n);
-}
-
-static void index_name_entry_free(git_index_name_entry *ne)
-{
- if (!ne)
- return;
- git__free(ne->ancestor);
- git__free(ne->ours);
- git__free(ne->theirs);
- git__free(ne);
-}
-
-int git_index_name_add(git_index *index,
- const char *ancestor, const char *ours, const char *theirs)
-{
- git_index_name_entry *conflict_name;
-
- assert((ancestor && ours) || (ancestor && theirs) || (ours && theirs));
-
- conflict_name = git__calloc(1, sizeof(git_index_name_entry));
- GITERR_CHECK_ALLOC(conflict_name);
-
- if ((ancestor && !(conflict_name->ancestor = git__strdup(ancestor))) ||
- (ours && !(conflict_name->ours = git__strdup(ours))) ||
- (theirs && !(conflict_name->theirs = git__strdup(theirs))) ||
- git_vector_insert(&index->names, conflict_name) < 0)
- {
- index_name_entry_free(conflict_name);
- return -1;
- }
-
- return 0;
-}
-
-void git_index_name_clear(git_index *index)
-{
- size_t i;
- git_index_name_entry *conflict_name;
-
- assert(index);
-
- git_vector_foreach(&index->names, i, conflict_name)
- index_name_entry_free(conflict_name);
-
- git_vector_clear(&index->names);
-}
-
-size_t git_index_reuc_entrycount(git_index *index)
-{
- assert(index);
- return index->reuc.length;
-}
-
-static int index_reuc_on_dup(void **old, void *new)
-{
- index_entry_reuc_free(*old);
- *old = new;
- return GIT_EEXISTS;
-}
-
-static int index_reuc_insert(
- git_index *index,
- git_index_reuc_entry *reuc)
-{
- int res;
-
- assert(index && reuc && reuc->path != NULL);
- assert(git_vector_is_sorted(&index->reuc));
-
- res = git_vector_insert_sorted(&index->reuc, reuc, &index_reuc_on_dup);
- return res == GIT_EEXISTS ? 0 : res;
-}
-
-int git_index_reuc_add(git_index *index, const char *path,
- int ancestor_mode, const git_oid *ancestor_oid,
- int our_mode, const git_oid *our_oid,
- int their_mode, const git_oid *their_oid)
-{
- git_index_reuc_entry *reuc = NULL;
- int error = 0;
-
- assert(index && path);
-
- if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode,
- ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 ||
- (error = index_reuc_insert(index, reuc)) < 0)
- index_entry_reuc_free(reuc);
-
- return error;
-}
-
-int git_index_reuc_find(size_t *at_pos, git_index *index, const char *path)
-{
- return git_vector_bsearch2(at_pos, &index->reuc, index->reuc_search, path);
-}
-
-const git_index_reuc_entry *git_index_reuc_get_bypath(
- git_index *index, const char *path)
-{
- size_t pos;
- assert(index && path);
-
- if (!index->reuc.length)
- return NULL;
-
- assert(git_vector_is_sorted(&index->reuc));
-
- if (git_index_reuc_find(&pos, index, path) < 0)
- return NULL;
-
- return git_vector_get(&index->reuc, pos);
-}
-
-const git_index_reuc_entry *git_index_reuc_get_byindex(
- git_index *index, size_t n)
-{
- assert(index);
- assert(git_vector_is_sorted(&index->reuc));
-
- return git_vector_get(&index->reuc, n);
-}
-
-int git_index_reuc_remove(git_index *index, size_t position)
-{
- int error;
- git_index_reuc_entry *reuc;
-
- assert(git_vector_is_sorted(&index->reuc));
-
- reuc = git_vector_get(&index->reuc, position);
- error = git_vector_remove(&index->reuc, position);
-
- if (!error)
- index_entry_reuc_free(reuc);
-
- return error;
-}
-
-void git_index_reuc_clear(git_index *index)
-{
- size_t i;
-
- assert(index);
-
- for (i = 0; i < index->reuc.length; ++i)
- index_entry_reuc_free(git__swap(index->reuc.contents[i], NULL));
-
- git_vector_clear(&index->reuc);
-}
-
-static int index_error_invalid(const char *message)
-{
- giterr_set(GITERR_INDEX, "Invalid data in index - %s", message);
- return -1;
-}
-
-static int read_reuc(git_index *index, const char *buffer, size_t size)
-{
- const char *endptr;
- size_t len;
- int i;
-
- /* If called multiple times, the vector might already be initialized */
- if (index->reuc._alloc_size == 0 &&
- git_vector_init(&index->reuc, 16, reuc_cmp) < 0)
- return -1;
-
- while (size) {
- git_index_reuc_entry *lost;
-
- len = p_strnlen(buffer, size) + 1;
- if (size <= len)
- return index_error_invalid("reading reuc entries");
-
- lost = reuc_entry_alloc(buffer);
- GITERR_CHECK_ALLOC(lost);
-
- size -= len;
- buffer += len;
-
- /* read 3 ASCII octal numbers for stage entries */
- for (i = 0; i < 3; i++) {
- int64_t tmp;
-
- if (git__strtol64(&tmp, buffer, &endptr, 8) < 0 ||
- !endptr || endptr == buffer || *endptr ||
- tmp < 0 || tmp > UINT32_MAX) {
- index_entry_reuc_free(lost);
- return index_error_invalid("reading reuc entry stage");
- }
-
- lost->mode[i] = (uint32_t)tmp;
-
- len = (endptr + 1) - buffer;
- if (size <= len) {
- index_entry_reuc_free(lost);
- return index_error_invalid("reading reuc entry stage");
- }
-
- size -= len;
- buffer += len;
- }
-
- /* read up to 3 OIDs for stage entries */
- for (i = 0; i < 3; i++) {
- if (!lost->mode[i])
- continue;
- if (size < 20) {
- index_entry_reuc_free(lost);
- return index_error_invalid("reading reuc entry oid");
- }
-
- git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
- size -= 20;
- buffer += 20;
- }
-
- /* entry was read successfully - insert into reuc vector */
- if (git_vector_insert(&index->reuc, lost) < 0)
- return -1;
- }
-
- /* entries are guaranteed to be sorted on-disk */
- git_vector_set_sorted(&index->reuc, true);
-
- return 0;
-}
-
-
-static int read_conflict_names(git_index *index, const char *buffer, size_t size)
-{
- size_t len;
-
- /* This gets called multiple times, the vector might already be initialized */
- if (index->names._alloc_size == 0 &&
- git_vector_init(&index->names, 16, conflict_name_cmp) < 0)
- return -1;
-
-#define read_conflict_name(ptr) \
- len = p_strnlen(buffer, size) + 1; \
- if (size < len) { \
- index_error_invalid("reading conflict name entries"); \
- goto out_err; \
- } \
- if (len == 1) \
- ptr = NULL; \
- else { \
- ptr = git__malloc(len); \
- GITERR_CHECK_ALLOC(ptr); \
- memcpy(ptr, buffer, len); \
- } \
- \
- buffer += len; \
- size -= len;
-
- while (size) {
- git_index_name_entry *conflict_name = git__calloc(1, sizeof(git_index_name_entry));
- GITERR_CHECK_ALLOC(conflict_name);
-
- read_conflict_name(conflict_name->ancestor);
- read_conflict_name(conflict_name->ours);
- read_conflict_name(conflict_name->theirs);
-
- if (git_vector_insert(&index->names, conflict_name) < 0)
- goto out_err;
-
- continue;
-
-out_err:
- git__free(conflict_name->ancestor);
- git__free(conflict_name->ours);
- git__free(conflict_name->theirs);
- git__free(conflict_name);
- return -1;
- }
-
-#undef read_conflict_name
-
- /* entries are guaranteed to be sorted on-disk */
- git_vector_set_sorted(&index->names, true);
-
- return 0;
-}
-
-static size_t read_entry(
- git_index_entry **out,
- git_index *index,
- const void *buffer,
- size_t buffer_size,
- const char **last)
-{
- size_t path_length, entry_size;
- const char *path_ptr;
- struct entry_short source;
- git_index_entry entry = {{0}};
- bool compressed = index->version >= INDEX_VERSION_NUMBER_COMP;
- char *tmp_path = NULL;
-
- if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
- return 0;
-
- /* buffer is not guaranteed to be aligned */
- memcpy(&source, buffer, sizeof(struct entry_short));
-
- entry.ctime.seconds = (git_time_t)ntohl(source.ctime.seconds);
- entry.ctime.nanoseconds = ntohl(source.ctime.nanoseconds);
- entry.mtime.seconds = (git_time_t)ntohl(source.mtime.seconds);
- entry.mtime.nanoseconds = ntohl(source.mtime.nanoseconds);
- entry.dev = ntohl(source.dev);
- entry.ino = ntohl(source.ino);
- entry.mode = ntohl(source.mode);
- entry.uid = ntohl(source.uid);
- entry.gid = ntohl(source.gid);
- entry.file_size = ntohl(source.file_size);
- git_oid_cpy(&entry.id, &source.oid);
- entry.flags = ntohs(source.flags);
-
- if (entry.flags & GIT_IDXENTRY_EXTENDED) {
- uint16_t flags_raw;
- size_t flags_offset;
-
- flags_offset = offsetof(struct entry_long, flags_extended);
- memcpy(&flags_raw, (const char *) buffer + flags_offset,
- sizeof(flags_raw));
- flags_raw = ntohs(flags_raw);
-
- memcpy(&entry.flags_extended, &flags_raw, sizeof(flags_raw));
- path_ptr = (const char *) buffer + offsetof(struct entry_long, path);
- } else
- path_ptr = (const char *) buffer + offsetof(struct entry_short, path);
-
- if (!compressed) {
- path_length = entry.flags & GIT_IDXENTRY_NAMEMASK;
-
- /* if this is a very long string, we must find its
- * real length without overflowing */
- if (path_length == 0xFFF) {
- const char *path_end;
-
- path_end = memchr(path_ptr, '\0', buffer_size);
- if (path_end == NULL)
- return 0;
-
- path_length = path_end - path_ptr;
- }
-
- if (entry.flags & GIT_IDXENTRY_EXTENDED)
- entry_size = long_entry_size(path_length);
- else
- entry_size = short_entry_size(path_length);
-
- if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
- return 0;
-
- entry.path = (char *)path_ptr;
- } else {
- size_t varint_len;
- size_t shared = git_decode_varint((const unsigned char *)path_ptr,
- &varint_len);
- size_t len = strlen(path_ptr + varint_len);
- size_t last_len = strlen(*last);
- size_t tmp_path_len;
-
- if (varint_len == 0)
- return index_error_invalid("incorrect prefix length");
-
- GITERR_CHECK_ALLOC_ADD(&tmp_path_len, shared, len + 1);
- tmp_path = git__malloc(tmp_path_len);
- GITERR_CHECK_ALLOC(tmp_path);
- memcpy(tmp_path, last, last_len);
- memcpy(tmp_path + last_len, path_ptr + varint_len, len);
- entry_size = long_entry_size(shared + len);
- entry.path = tmp_path;
- }
-
- if (index_entry_dup(out, index, &entry) < 0) {
- git__free(tmp_path);
- return 0;
- }
-
- git__free(tmp_path);
- return entry_size;
-}
-
-static int read_header(struct index_header *dest, const void *buffer)
-{
- const struct index_header *source = buffer;
-
- dest->signature = ntohl(source->signature);
- if (dest->signature != INDEX_HEADER_SIG)
- return index_error_invalid("incorrect header signature");
-
- dest->version = ntohl(source->version);
- if (dest->version < INDEX_VERSION_NUMBER_LB ||
- dest->version > INDEX_VERSION_NUMBER_UB)
- return index_error_invalid("incorrect header version");
-
- dest->entry_count = ntohl(source->entry_count);
- return 0;
-}
-
-static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size)
-{
- struct index_extension dest;
- size_t total_size;
-
- /* buffer is not guaranteed to be aligned */
- memcpy(&dest, buffer, sizeof(struct index_extension));
- dest.extension_size = ntohl(dest.extension_size);
-
- total_size = dest.extension_size + sizeof(struct index_extension);
-
- if (dest.extension_size > total_size ||
- buffer_size < total_size ||
- buffer_size - total_size < INDEX_FOOTER_SIZE)
- return 0;
-
- /* optional extension */
- if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {
- /* tree cache */
- if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {
- if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size, &index->tree_pool) < 0)
- return 0;
- } else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
- if (read_reuc(index, buffer + 8, dest.extension_size) < 0)
- return 0;
- } else if (memcmp(dest.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4) == 0) {
- if (read_conflict_names(index, buffer + 8, dest.extension_size) < 0)
- return 0;
- }
- /* else, unsupported extension. We cannot parse this, but we can skip
- * it by returning `total_size */
- } else {
- /* we cannot handle non-ignorable extensions;
- * in fact they aren't even defined in the standard */
- return 0;
- }
-
- return total_size;
-}
-
-static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
-{
- int error = 0;
- unsigned int i;
- struct index_header header = { 0 };
- git_oid checksum_calculated, checksum_expected;
- const char **last = NULL;
- const char *empty = "";
-
-#define seek_forward(_increase) { \
- if (_increase >= buffer_size) { \
- error = index_error_invalid("ran out of data while parsing"); \
- goto done; } \
- buffer += _increase; \
- buffer_size -= _increase;\
-}
-
- if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE)
- return index_error_invalid("insufficient buffer space");
-
- /* Precalculate the SHA1 of the files's contents -- we'll match it to
- * the provided SHA1 in the footer */
- git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE);
-
- /* Parse header */
- if ((error = read_header(&header, buffer)) < 0)
- return error;
-
- index->version = header.version;
- if (index->version >= INDEX_VERSION_NUMBER_COMP)
- last = ∅
-
- seek_forward(INDEX_HEADER_SIZE);
-
- assert(!index->entries.length);
-
- if (index->ignore_case)
- kh_resize(idxicase, (khash_t(idxicase) *) index->entries_map, header.entry_count);
- else
- kh_resize(idx, index->entries_map, header.entry_count);
-
- /* Parse all the entries */
- for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
- git_index_entry *entry;
- size_t entry_size = read_entry(&entry, index, buffer, buffer_size, last);
-
- /* 0 bytes read means an object corruption */
- if (entry_size == 0) {
- error = index_error_invalid("invalid entry");
- goto done;
- }
-
- if ((error = git_vector_insert(&index->entries, entry)) < 0) {
- index_entry_free(entry);
- goto done;
- }
-
- INSERT_IN_MAP(index, entry, error);
-
- if (error < 0) {
- index_entry_free(entry);
- goto done;
- }
- error = 0;
-
- seek_forward(entry_size);
- }
-
- if (i != header.entry_count) {
- error = index_error_invalid("header entries changed while parsing");
- goto done;
- }
-
- /* There's still space for some extensions! */
- while (buffer_size > INDEX_FOOTER_SIZE) {
- size_t extension_size;
-
- extension_size = read_extension(index, buffer, buffer_size);
-
- /* see if we have read any bytes from the extension */
- if (extension_size == 0) {
- error = index_error_invalid("extension is truncated");
- goto done;
- }
-
- seek_forward(extension_size);
- }
-
- if (buffer_size != INDEX_FOOTER_SIZE) {
- error = index_error_invalid(
- "buffer size does not match index footer size");
- goto done;
- }
-
- /* 160-bit SHA-1 over the content of the index file before this checksum. */
- git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer);
-
- if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0) {
- error = index_error_invalid(
- "calculated checksum does not match expected");
- goto done;
- }
-
- git_oid_cpy(&index->checksum, &checksum_calculated);
-
-#undef seek_forward
-
- /* Entries are stored case-sensitively on disk, so re-sort now if
- * in-memory index is supposed to be case-insensitive
- */
- git_vector_set_sorted(&index->entries, !index->ignore_case);
- git_vector_sort(&index->entries);
-
-done:
- return error;
-}
-
-static bool is_index_extended(git_index *index)
-{
- size_t i, extended;
- git_index_entry *entry;
-
- extended = 0;
-
- git_vector_foreach(&index->entries, i, entry) {
- entry->flags &= ~GIT_IDXENTRY_EXTENDED;
- if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) {
- extended++;
- entry->flags |= GIT_IDXENTRY_EXTENDED;
- }
- }
-
- return (extended > 0);
-}
-
-static int write_disk_entry(git_filebuf *file, git_index_entry *entry, const char **last)
-{
- void *mem = NULL;
- struct entry_short *ondisk;
- size_t path_len, disk_size;
- char *path;
- const char *path_start = entry->path;
- size_t same_len = 0;
-
- path_len = ((struct entry_internal *)entry)->pathlen;
-
- if (last) {
- const char *last_c = *last;
-
- while (*path_start == *last_c) {
- if (!*path_start || !*last_c)
- break;
- ++path_start;
- ++last_c;
- ++same_len;
- }
- path_len -= same_len;
- *last = entry->path;
- }
-
- if (entry->flags & GIT_IDXENTRY_EXTENDED)
- disk_size = long_entry_size(path_len);
- else
- disk_size = short_entry_size(path_len);
-
- if (git_filebuf_reserve(file, &mem, disk_size) < 0)
- return -1;
-
- ondisk = (struct entry_short *)mem;
-
- memset(ondisk, 0x0, disk_size);
-
- /**
- * Yes, we have to truncate.
- *
- * The on-disk format for Index entries clearly defines
- * the time and size fields to be 4 bytes each -- so even if
- * we store these values with 8 bytes on-memory, they must
- * be truncated to 4 bytes before writing to disk.
- *
- * In 2038 I will be either too dead or too rich to care about this
- */
- ondisk->ctime.seconds = htonl((uint32_t)entry->ctime.seconds);
- ondisk->mtime.seconds = htonl((uint32_t)entry->mtime.seconds);
- ondisk->ctime.nanoseconds = htonl(entry->ctime.nanoseconds);
- ondisk->mtime.nanoseconds = htonl(entry->mtime.nanoseconds);
- ondisk->dev = htonl(entry->dev);
- ondisk->ino = htonl(entry->ino);
- ondisk->mode = htonl(entry->mode);
- ondisk->uid = htonl(entry->uid);
- ondisk->gid = htonl(entry->gid);
- ondisk->file_size = htonl((uint32_t)entry->file_size);
-
- git_oid_cpy(&ondisk->oid, &entry->id);
-
- ondisk->flags = htons(entry->flags);
-
- if (entry->flags & GIT_IDXENTRY_EXTENDED) {
- struct entry_long *ondisk_ext;
- ondisk_ext = (struct entry_long *)ondisk;
- ondisk_ext->flags_extended = htons(entry->flags_extended &
- GIT_IDXENTRY_EXTENDED_FLAGS);
- path = ondisk_ext->path;
- }
- else
- path = ondisk->path;
-
- if (last) {
- path += git_encode_varint((unsigned char *) path,
- disk_size,
- path_len - same_len);
- }
- memcpy(path, path_start, path_len);
-
- return 0;
-}
-
-static int write_entries(git_index *index, git_filebuf *file)
-{
- int error = 0;
- size_t i;
- git_vector case_sorted, *entries;
- git_index_entry *entry;
- const char **last = NULL;
- const char *empty = "";
-
- /* If index->entries is sorted case-insensitively, then we need
- * to re-sort it case-sensitively before writing */
- if (index->ignore_case) {
- git_vector_dup(&case_sorted, &index->entries, git_index_entry_cmp);
- git_vector_sort(&case_sorted);
- entries = &case_sorted;
- } else {
- entries = &index->entries;
- }
-
- if (index->version >= INDEX_VERSION_NUMBER_COMP)
- last = ∅
-
- git_vector_foreach(entries, i, entry)
- if ((error = write_disk_entry(file, entry, last)) < 0)
- break;
-
- if (index->ignore_case)
- git_vector_free(&case_sorted);
-
- return error;
-}
-
-static int write_extension(git_filebuf *file, struct index_extension *header, git_buf *data)
-{
- struct index_extension ondisk;
-
- memset(&ondisk, 0x0, sizeof(struct index_extension));
- memcpy(&ondisk, header, 4);
- ondisk.extension_size = htonl(header->extension_size);
-
- git_filebuf_write(file, &ondisk, sizeof(struct index_extension));
- return git_filebuf_write(file, data->ptr, data->size);
-}
-
-static int create_name_extension_data(git_buf *name_buf, git_index_name_entry *conflict_name)
-{
- int error = 0;
-
- if (conflict_name->ancestor == NULL)
- error = git_buf_put(name_buf, "\0", 1);
- else
- error = git_buf_put(name_buf, conflict_name->ancestor, strlen(conflict_name->ancestor) + 1);
-
- if (error != 0)
- goto on_error;
-
- if (conflict_name->ours == NULL)
- error = git_buf_put(name_buf, "\0", 1);
- else
- error = git_buf_put(name_buf, conflict_name->ours, strlen(conflict_name->ours) + 1);
-
- if (error != 0)
- goto on_error;
-
- if (conflict_name->theirs == NULL)
- error = git_buf_put(name_buf, "\0", 1);
- else
- error = git_buf_put(name_buf, conflict_name->theirs, strlen(conflict_name->theirs) + 1);
-
-on_error:
- return error;
-}
-
-static int write_name_extension(git_index *index, git_filebuf *file)
-{
- git_buf name_buf = GIT_BUF_INIT;
- git_vector *out = &index->names;
- git_index_name_entry *conflict_name;
- struct index_extension extension;
- size_t i;
- int error = 0;
-
- git_vector_foreach(out, i, conflict_name) {
- if ((error = create_name_extension_data(&name_buf, conflict_name)) < 0)
- goto done;
- }
-
- memset(&extension, 0x0, sizeof(struct index_extension));
- memcpy(&extension.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4);
- extension.extension_size = (uint32_t)name_buf.size;
-
- error = write_extension(file, &extension, &name_buf);
-
- git_buf_free(&name_buf);
-
-done:
- return error;
-}
-
-static int create_reuc_extension_data(git_buf *reuc_buf, git_index_reuc_entry *reuc)
-{
- int i;
- int error = 0;
-
- if ((error = git_buf_put(reuc_buf, reuc->path, strlen(reuc->path) + 1)) < 0)
- return error;
-
- for (i = 0; i < 3; i++) {
- if ((error = git_buf_printf(reuc_buf, "%o", reuc->mode[i])) < 0 ||
- (error = git_buf_put(reuc_buf, "\0", 1)) < 0)
- return error;
- }
-
- for (i = 0; i < 3; i++) {
- if (reuc->mode[i] && (error = git_buf_put(reuc_buf, (char *)&reuc->oid[i].id, GIT_OID_RAWSZ)) < 0)
- return error;
- }
-
- return 0;
-}
-
-static int write_reuc_extension(git_index *index, git_filebuf *file)
-{
- git_buf reuc_buf = GIT_BUF_INIT;
- git_vector *out = &index->reuc;
- git_index_reuc_entry *reuc;
- struct index_extension extension;
- size_t i;
- int error = 0;
-
- git_vector_foreach(out, i, reuc) {
- if ((error = create_reuc_extension_data(&reuc_buf, reuc)) < 0)
- goto done;
- }
-
- memset(&extension, 0x0, sizeof(struct index_extension));
- memcpy(&extension.signature, INDEX_EXT_UNMERGED_SIG, 4);
- extension.extension_size = (uint32_t)reuc_buf.size;
-
- error = write_extension(file, &extension, &reuc_buf);
-
- git_buf_free(&reuc_buf);
-
-done:
- return error;
-}
-
-static int write_tree_extension(git_index *index, git_filebuf *file)
-{
- struct index_extension extension;
- git_buf buf = GIT_BUF_INIT;
- int error;
-
- if (index->tree == NULL)
- return 0;
-
- if ((error = git_tree_cache_write(&buf, index->tree)) < 0)
- return error;
-
- memset(&extension, 0x0, sizeof(struct index_extension));
- memcpy(&extension.signature, INDEX_EXT_TREECACHE_SIG, 4);
- extension.extension_size = (uint32_t)buf.size;
-
- error = write_extension(file, &extension, &buf);
-
- git_buf_free(&buf);
-
- return error;
-}
-
-static void clear_uptodate(git_index *index)
-{
- git_index_entry *entry;
- size_t i;
-
- git_vector_foreach(&index->entries, i, entry)
- entry->flags_extended &= ~GIT_IDXENTRY_UPTODATE;
-}
-
-static int write_index(git_oid *checksum, git_index *index, git_filebuf *file)
-{
- git_oid hash_final;
- struct index_header header;
- bool is_extended;
- uint32_t index_version_number;
-
- assert(index && file);
-
- if (index->version <= INDEX_VERSION_NUMBER_EXT) {
- is_extended = is_index_extended(index);
- index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER_LB;
- } else {
- index_version_number = index->version;
- }
-
- header.signature = htonl(INDEX_HEADER_SIG);
- header.version = htonl(index_version_number);
- header.entry_count = htonl((uint32_t)index->entries.length);
-
- if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0)
- return -1;
-
- if (write_entries(index, file) < 0)
- return -1;
-
- /* write the tree cache extension */
- if (index->tree != NULL && write_tree_extension(index, file) < 0)
- return -1;
-
- /* write the rename conflict extension */
- if (index->names.length > 0 && write_name_extension(index, file) < 0)
- return -1;
-
- /* write the reuc extension */
- if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0)
- return -1;
-
- /* get out the hash for all the contents we've appended to the file */
- git_filebuf_hash(&hash_final, file);
- git_oid_cpy(checksum, &hash_final);
-
- /* write it at the end of the file */
- if (git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ) < 0)
- return -1;
-
- /* file entries are no longer up to date */
- clear_uptodate(index);
-
- return 0;
-}
-
-int git_index_entry_stage(const git_index_entry *entry)
-{
- return GIT_IDXENTRY_STAGE(entry);
-}
-
-int git_index_entry_is_conflict(const git_index_entry *entry)
-{
- return (GIT_IDXENTRY_STAGE(entry) > 0);
-}
-
-typedef struct read_tree_data {
- git_index *index;
- git_vector *old_entries;
- git_vector *new_entries;
- git_vector_cmp entry_cmp;
- git_tree_cache *tree;
-} read_tree_data;
-
-static int read_tree_cb(
- const char *root, const git_tree_entry *tentry, void *payload)
-{
- read_tree_data *data = payload;
- git_index_entry *entry = NULL, *old_entry;
- git_buf path = GIT_BUF_INIT;
- size_t pos;
-
- if (git_tree_entry__is_tree(tentry))
- return 0;
-
- if (git_buf_joinpath(&path, root, tentry->filename) < 0)
- return -1;
-
- if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr, false) < 0)
- return -1;
-
- entry->mode = tentry->attr;
- git_oid_cpy(&entry->id, git_tree_entry_id(tentry));
-
- /* look for corresponding old entry and copy data to new entry */
- if (data->old_entries != NULL &&
- !index_find_in_entries(
- &pos, data->old_entries, data->entry_cmp, path.ptr, 0, 0) &&
- (old_entry = git_vector_get(data->old_entries, pos)) != NULL &&
- entry->mode == old_entry->mode &&
- git_oid_equal(&entry->id, &old_entry->id))
- {
- index_entry_cpy(entry, old_entry);
- entry->flags_extended = 0;
- }
-
- index_entry_adjust_namemask(entry, path.size);
- git_buf_free(&path);
-
- if (git_vector_insert(data->new_entries, entry) < 0) {
- index_entry_free(entry);
- return -1;
- }
-
- return 0;
-}
-
-int git_index_read_tree(git_index *index, const git_tree *tree)
-{
- int error = 0;
- git_vector entries = GIT_VECTOR_INIT;
- git_idxmap *entries_map;
- read_tree_data data;
- size_t i;
- git_index_entry *e;
-
- if (git_idxmap_alloc(&entries_map) < 0)
- return -1;
-
- git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */
-
- data.index = index;
- data.old_entries = &index->entries;
- data.new_entries = &entries;
- data.entry_cmp = index->entries_search;
-
- index->tree = NULL;
- git_pool_clear(&index->tree_pool);
-
- git_vector_sort(&index->entries);
-
- if ((error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data)) < 0)
- goto cleanup;
-
- if (index->ignore_case)
- kh_resize(idxicase, (khash_t(idxicase) *) entries_map, entries.length);
- else
- kh_resize(idx, entries_map, entries.length);
-
- git_vector_foreach(&entries, i, e) {
- INSERT_IN_MAP_EX(index, entries_map, e, error);
-
- if (error < 0) {
- giterr_set(GITERR_INDEX, "failed to insert entry into map");
- return error;
- }
- }
-
- error = 0;
-
- git_vector_sort(&entries);
-
- if ((error = git_index_clear(index)) < 0) {
- /* well, this isn't good */;
- } else {
- git_vector_swap(&entries, &index->entries);
- entries_map = git__swap(index->entries_map, entries_map);
- }
-
-cleanup:
- git_vector_free(&entries);
- git_idxmap_free(entries_map);
- if (error < 0)
- return error;
-
- error = git_tree_cache_read_tree(&index->tree, tree, &index->tree_pool);
-
- return error;
-}
-
-static int git_index_read_iterator(
- git_index *index,
- git_iterator *new_iterator,
- size_t new_length_hint)
-{
- git_vector new_entries = GIT_VECTOR_INIT,
- remove_entries = GIT_VECTOR_INIT;
- git_idxmap *new_entries_map = NULL;
- git_iterator *index_iterator = NULL;
- git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
- const git_index_entry *old_entry, *new_entry;
- git_index_entry *entry;
- size_t i;
- int error;
-
- assert((new_iterator->flags & GIT_ITERATOR_DONT_IGNORE_CASE));
-
- if ((error = git_vector_init(&new_entries, new_length_hint, index->entries._cmp)) < 0 ||
- (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0 ||
- (error = git_idxmap_alloc(&new_entries_map)) < 0)
- goto done;
-
- if (index->ignore_case && new_length_hint)
- kh_resize(idxicase, (khash_t(idxicase) *) new_entries_map, new_length_hint);
- else if (new_length_hint)
- kh_resize(idx, new_entries_map, new_length_hint);
-
- opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE |
- GIT_ITERATOR_INCLUDE_CONFLICTS;
-
- if ((error = git_iterator_for_index(&index_iterator,
- git_index_owner(index), index, &opts)) < 0 ||
- ((error = git_iterator_current(&old_entry, index_iterator)) < 0 &&
- error != GIT_ITEROVER) ||
- ((error = git_iterator_current(&new_entry, new_iterator)) < 0 &&
- error != GIT_ITEROVER))
- goto done;
-
- while (true) {
- git_index_entry
- *dup_entry = NULL,
- *add_entry = NULL,
- *remove_entry = NULL;
- int diff;
-
- error = 0;
-
- if (old_entry && new_entry)
- diff = git_index_entry_cmp(old_entry, new_entry);
- else if (!old_entry && new_entry)
- diff = 1;
- else if (old_entry && !new_entry)
- diff = -1;
- else
- break;
-
- if (diff < 0) {
- remove_entry = (git_index_entry *)old_entry;
- } else if (diff > 0) {
- dup_entry = (git_index_entry *)new_entry;
- } else {
- /* Path and stage are equal, if the OID is equal, keep it to
- * keep the stat cache data.
- */
- if (git_oid_equal(&old_entry->id, &new_entry->id) &&
- old_entry->mode == new_entry->mode) {
- add_entry = (git_index_entry *)old_entry;
- } else {
- dup_entry = (git_index_entry *)new_entry;
- remove_entry = (git_index_entry *)old_entry;
- }
- }
-
- if (dup_entry) {
- if ((error = index_entry_dup_nocache(&add_entry, index, dup_entry)) < 0)
- goto done;
-
- index_entry_adjust_namemask(add_entry,
- ((struct entry_internal *)add_entry)->pathlen);
- }
-
- /* invalidate this path in the tree cache if this is new (to
- * invalidate the parent trees)
- */
- if (dup_entry && !remove_entry && index->tree)
- git_tree_cache_invalidate_path(index->tree, dup_entry->path);
-
- if (add_entry) {
- if ((error = git_vector_insert(&new_entries, add_entry)) == 0)
- INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error);
- }
-
- if (remove_entry && error >= 0)
- error = git_vector_insert(&remove_entries, remove_entry);
-
- if (error < 0) {
- giterr_set(GITERR_INDEX, "failed to insert entry");
- goto done;
- }
-
- if (diff <= 0) {
- if ((error = git_iterator_advance(&old_entry, index_iterator)) < 0 &&
- error != GIT_ITEROVER)
- goto done;
- }
-
- if (diff >= 0) {
- if ((error = git_iterator_advance(&new_entry, new_iterator)) < 0 &&
- error != GIT_ITEROVER)
- goto done;
- }
- }
-
- git_index_name_clear(index);
- git_index_reuc_clear(index);
-
- git_vector_swap(&new_entries, &index->entries);
- new_entries_map = git__swap(index->entries_map, new_entries_map);
-
- git_vector_foreach(&remove_entries, i, entry) {
- if (index->tree)
- git_tree_cache_invalidate_path(index->tree, entry->path);
-
- index_entry_free(entry);
- }
-
- clear_uptodate(index);
-
- error = 0;
-
-done:
- git_idxmap_free(new_entries_map);
- git_vector_free(&new_entries);
- git_vector_free(&remove_entries);
- git_iterator_free(index_iterator);
- return error;
-}
-
-int git_index_read_index(
- git_index *index,
- const git_index *new_index)
-{
- git_iterator *new_iterator = NULL;
- git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
- int error;
-
- opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE |
- GIT_ITERATOR_INCLUDE_CONFLICTS;
-
- if ((error = git_iterator_for_index(&new_iterator,
- git_index_owner(new_index), (git_index *)new_index, &opts)) < 0 ||
- (error = git_index_read_iterator(index, new_iterator,
- new_index->entries.length)) < 0)
- goto done;
-
-done:
- git_iterator_free(new_iterator);
- return error;
-}
-
-git_repository *git_index_owner(const git_index *index)
-{
- return INDEX_OWNER(index);
-}
-
-enum {
- INDEX_ACTION_NONE = 0,
- INDEX_ACTION_UPDATE = 1,
- INDEX_ACTION_REMOVE = 2,
- INDEX_ACTION_ADDALL = 3,
-};
-
-int git_index_add_all(
- git_index *index,
- const git_strarray *paths,
- unsigned int flags,
- git_index_matched_path_cb cb,
- void *payload)
-{
- int error;
- git_repository *repo;
- git_iterator *wditer = NULL;
- git_pathspec ps;
- bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0;
-
- assert(index);
-
- repo = INDEX_OWNER(index);
- if ((error = git_repository__ensure_not_bare(repo, "index add all")) < 0)
- return error;
-
- if ((error = git_pathspec__init(&ps, paths)) < 0)
- return error;
-
- /* optionally check that pathspec doesn't mention any ignored files */
- if ((flags & GIT_INDEX_ADD_CHECK_PATHSPEC) != 0 &&
- (flags & GIT_INDEX_ADD_FORCE) == 0 &&
- (error = git_ignore__check_pathspec_for_exact_ignores(
- repo, &ps.pathspec, no_fnmatch)) < 0)
- goto cleanup;
-
- error = index_apply_to_wd_diff(index, INDEX_ACTION_ADDALL, paths, flags, cb, payload);
-
- if (error)
- giterr_set_after_callback(error);
-
-cleanup:
- git_iterator_free(wditer);
- git_pathspec__clear(&ps);
-
- return error;
-}
-
-struct foreach_diff_data {
- git_index *index;
- const git_pathspec *pathspec;
- unsigned int flags;
- git_index_matched_path_cb cb;
- void *payload;
-};
-
-static int apply_each_file(const git_diff_delta *delta, float progress, void *payload)
-{
- struct foreach_diff_data *data = payload;
- const char *match, *path;
- int error = 0;
-
- GIT_UNUSED(progress);
-
- path = delta->old_file.path;
-
- /* We only want those which match the pathspecs */
- if (!git_pathspec__match(
- &data->pathspec->pathspec, path, false, (bool)data->index->ignore_case,
- &match, NULL))
- return 0;
-
- if (data->cb)
- error = data->cb(path, match, data->payload);
-
- if (error > 0) /* skip this entry */
- return 0;
- if (error < 0) /* actual error */
- return error;
-
- /* If the workdir item does not exist, remove it from the index. */
- if ((delta->new_file.flags & GIT_DIFF_FLAG_EXISTS) == 0)
- error = git_index_remove_bypath(data->index, path);
- else
- error = git_index_add_bypath(data->index, delta->new_file.path);
-
- return error;
-}
-
-static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths,
- unsigned int flags,
- git_index_matched_path_cb cb, void *payload)
-{
- int error;
- git_diff *diff;
- git_pathspec ps;
- git_repository *repo;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- struct foreach_diff_data data = {
- index,
- NULL,
- flags,
- cb,
- payload,
- };
-
- assert(index);
- assert(action == INDEX_ACTION_UPDATE || action == INDEX_ACTION_ADDALL);
-
- repo = INDEX_OWNER(index);
-
- if (!repo) {
- return create_index_error(-1,
- "cannot run update; the index is not backed up by a repository.");
- }
-
- /*
- * We do the matching ourselves intead of passing the list to
- * diff because we want to tell the callback which one
- * matched, which we do not know if we ask diff to filter for us.
- */
- if ((error = git_pathspec__init(&ps, paths)) < 0)
- return error;
-
- opts.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
- if (action == INDEX_ACTION_ADDALL) {
- opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
- GIT_DIFF_RECURSE_UNTRACKED_DIRS;
-
- if (flags == GIT_INDEX_ADD_FORCE)
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
- }
-
- if ((error = git_diff_index_to_workdir(&diff, repo, index, &opts)) < 0)
- goto cleanup;
-
- data.pathspec = &ps;
- error = git_diff_foreach(diff, apply_each_file, NULL, NULL, NULL, &data);
- git_diff_free(diff);
-
- if (error) /* make sure error is set if callback stopped iteration */
- giterr_set_after_callback(error);
-
-cleanup:
- git_pathspec__clear(&ps);
- return error;
-}
-
-static int index_apply_to_all(
- git_index *index,
- int action,
- const git_strarray *paths,
- git_index_matched_path_cb cb,
- void *payload)
-{
- int error = 0;
- size_t i;
- git_pathspec ps;
- const char *match;
- git_buf path = GIT_BUF_INIT;
-
- assert(index);
-
- if ((error = git_pathspec__init(&ps, paths)) < 0)
- return error;
-
- git_vector_sort(&index->entries);
-
- for (i = 0; !error && i < index->entries.length; ++i) {
- git_index_entry *entry = git_vector_get(&index->entries, i);
-
- /* check if path actually matches */
- if (!git_pathspec__match(
- &ps.pathspec, entry->path, false, (bool)index->ignore_case,
- &match, NULL))
- continue;
-
- /* issue notification callback if requested */
- if (cb && (error = cb(entry->path, match, payload)) != 0) {
- if (error > 0) { /* return > 0 means skip this one */
- error = 0;
- continue;
- }
- if (error < 0) /* return < 0 means abort */
- break;
- }
-
- /* index manipulation may alter entry, so don't depend on it */
- if ((error = git_buf_sets(&path, entry->path)) < 0)
- break;
-
- switch (action) {
- case INDEX_ACTION_NONE:
- break;
- case INDEX_ACTION_UPDATE:
- error = git_index_add_bypath(index, path.ptr);
-
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
-
- error = git_index_remove_bypath(index, path.ptr);
-
- if (!error) /* back up foreach if we removed this */
- i--;
- }
- break;
- case INDEX_ACTION_REMOVE:
- if (!(error = git_index_remove_bypath(index, path.ptr)))
- i--; /* back up foreach if we removed this */
- break;
- default:
- giterr_set(GITERR_INVALID, "Unknown index action %d", action);
- error = -1;
- break;
- }
- }
-
- git_buf_free(&path);
- git_pathspec__clear(&ps);
-
- return error;
-}
-
-int git_index_remove_all(
- git_index *index,
- const git_strarray *pathspec,
- git_index_matched_path_cb cb,
- void *payload)
-{
- int error = index_apply_to_all(
- index, INDEX_ACTION_REMOVE, pathspec, cb, payload);
-
- if (error) /* make sure error is set if callback stopped iteration */
- giterr_set_after_callback(error);
-
- return error;
-}
-
-int git_index_update_all(
- git_index *index,
- const git_strarray *pathspec,
- git_index_matched_path_cb cb,
- void *payload)
-{
- int error = index_apply_to_wd_diff(index, INDEX_ACTION_UPDATE, pathspec, 0, cb, payload);
- if (error) /* make sure error is set if callback stopped iteration */
- giterr_set_after_callback(error);
-
- return error;
-}
-
-int git_index_snapshot_new(git_vector *snap, git_index *index)
-{
- int error;
-
- GIT_REFCOUNT_INC(index);
-
- git_atomic_inc(&index->readers);
- git_vector_sort(&index->entries);
-
- error = git_vector_dup(snap, &index->entries, index->entries._cmp);
-
- if (error < 0)
- git_index_free(index);
-
- return error;
-}
-
-void git_index_snapshot_release(git_vector *snap, git_index *index)
-{
- git_vector_free(snap);
-
- git_atomic_dec(&index->readers);
-
- git_index_free(index);
-}
-
-int git_index_snapshot_find(
- size_t *out, git_vector *entries, git_vector_cmp entry_srch,
- const char *path, size_t path_len, int stage)
-{
- return index_find_in_entries(out, entries, entry_srch, path, path_len, stage);
-}
-
-int git_indexwriter_init(
- git_indexwriter *writer,
- git_index *index)
-{
- int error;
-
- GIT_REFCOUNT_INC(index);
-
- writer->index = index;
-
- if (!index->index_file_path)
- return create_index_error(-1,
- "Failed to write index: The index is in-memory only");
-
- if ((error = git_filebuf_open(
- &writer->file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) {
-
- if (error == GIT_ELOCKED)
- giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrent or crashed process");
-
- return error;
- }
-
- writer->should_write = 1;
-
- return 0;
-}
-
-int git_indexwriter_init_for_operation(
- git_indexwriter *writer,
- git_repository *repo,
- unsigned int *checkout_strategy)
-{
- git_index *index;
- int error;
-
- if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
- (error = git_indexwriter_init(writer, index)) < 0)
- return error;
-
- writer->should_write = (*checkout_strategy & GIT_CHECKOUT_DONT_WRITE_INDEX) == 0;
- *checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX;
-
- return 0;
-}
-
-int git_indexwriter_commit(git_indexwriter *writer)
-{
- int error;
- git_oid checksum = {{ 0 }};
-
- if (!writer->should_write)
- return 0;
-
- git_vector_sort(&writer->index->entries);
- git_vector_sort(&writer->index->reuc);
-
- if ((error = write_index(&checksum, writer->index, &writer->file)) < 0) {
- git_indexwriter_cleanup(writer);
- return error;
- }
-
- if ((error = git_filebuf_commit(&writer->file)) < 0)
- return error;
-
- if ((error = git_futils_filestamp_check(
- &writer->index->stamp, writer->index->index_file_path)) < 0) {
- giterr_set(GITERR_OS, "Could not read index timestamp");
- return -1;
- }
-
- writer->index->on_disk = 1;
- git_oid_cpy(&writer->index->checksum, &checksum);
-
- git_index_free(writer->index);
- writer->index = NULL;
-
- return 0;
-}
-
-void git_indexwriter_cleanup(git_indexwriter *writer)
-{
- git_filebuf_cleanup(&writer->file);
-
- git_index_free(writer->index);
- writer->index = NULL;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_index_h__
-#define INCLUDE_index_h__
-
-#include "fileops.h"
-#include "filebuf.h"
-#include "vector.h"
-#include "idxmap.h"
-#include "tree-cache.h"
-#include "git2/odb.h"
-#include "git2/index.h"
-
-#define GIT_INDEX_FILE "index"
-#define GIT_INDEX_FILE_MODE 0666
-
-struct git_index {
- git_refcount rc;
-
- char *index_file_path;
- git_futils_filestamp stamp;
- git_oid checksum; /* checksum at the end of the file */
-
- git_vector entries;
- git_idxmap *entries_map;
-
- git_vector deleted; /* deleted entries if readers > 0 */
- git_atomic readers; /* number of active iterators */
-
- unsigned int on_disk:1;
- unsigned int ignore_case:1;
- unsigned int distrust_filemode:1;
- unsigned int no_symlinks:1;
-
- git_tree_cache *tree;
- git_pool tree_pool;
-
- git_vector names;
- git_vector reuc;
-
- git_vector_cmp entries_cmp_path;
- git_vector_cmp entries_search;
- git_vector_cmp entries_search_path;
- git_vector_cmp reuc_search;
-
- unsigned int version;
-};
-
-struct git_index_conflict_iterator {
- git_index *index;
- size_t cur;
-};
-
-extern void git_index_entry__init_from_stat(
- git_index_entry *entry, struct stat *st, bool trust_mode);
-
-/* Index entry comparison functions for array sorting */
-extern int git_index_entry_cmp(const void *a, const void *b);
-extern int git_index_entry_icmp(const void *a, const void *b);
-
-/* Index entry search functions for search using a search spec */
-extern int git_index_entry_srch(const void *a, const void *b);
-extern int git_index_entry_isrch(const void *a, const void *b);
-
-/* Index time handling functions */
-GIT_INLINE(bool) git_index_time_eq(const git_index_time *one, const git_index_time *two)
-{
- if (one->seconds != two->seconds)
- return false;
-
-#ifdef GIT_USE_NSEC
- if (one->nanoseconds != two->nanoseconds)
- return false;
-#endif
-
- return true;
-}
-
-/*
- * Test if the given index time is newer than the given existing index entry.
- * If the timestamps are exactly equivalent, then the given index time is
- * considered "racily newer" than the existing index entry.
- */
-GIT_INLINE(bool) git_index_entry_newer_than_index(
- const git_index_entry *entry, git_index *index)
-{
- /* If we never read the index, we can't have this race either */
- if (!index || index->stamp.mtime.tv_sec == 0)
- return false;
-
- /* If the timestamp is the same or newer than the index, it's racy */
-#if defined(GIT_USE_NSEC)
- if ((int32_t)index->stamp.mtime.tv_sec < entry->mtime.seconds)
- return true;
- else if ((int32_t)index->stamp.mtime.tv_sec > entry->mtime.seconds)
- return false;
- else
- return (uint32_t)index->stamp.mtime.tv_nsec <= entry->mtime.nanoseconds;
-#else
- return ((int32_t)index->stamp.mtime.tv_sec) <= entry->mtime.seconds;
-#endif
-}
-
-/* Search index for `path`, returning GIT_ENOTFOUND if it does not exist
- * (but not setting an error message).
- *
- * `at_pos` is set to the position where it is or would be inserted.
- * Pass `path_len` as strlen of path or 0 to call strlen internally.
- */
-extern int git_index__find_pos(
- size_t *at_pos, git_index *index, const char *path, size_t path_len, int stage);
-
-extern int git_index__fill(git_index *index, const git_vector *source_entries);
-
-extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
-
-extern unsigned int git_index__create_mode(unsigned int mode);
-
-GIT_INLINE(const git_futils_filestamp *) git_index__filestamp(git_index *index)
-{
- return &index->stamp;
-}
-
-extern int git_index__changed_relative_to(git_index *index, const git_oid *checksum);
-
-/* Copy the current entries vector *and* increment the index refcount.
- * Call `git_index__release_snapshot` when done.
- */
-extern int git_index_snapshot_new(git_vector *snap, git_index *index);
-extern void git_index_snapshot_release(git_vector *snap, git_index *index);
-
-/* Allow searching in a snapshot; entries must already be sorted! */
-extern int git_index_snapshot_find(
- size_t *at_pos, git_vector *snap, git_vector_cmp entry_srch,
- const char *path, size_t path_len, int stage);
-
-/* Replace an index with a new index */
-int git_index_read_index(git_index *index, const git_index *new_index);
-
-typedef struct {
- git_index *index;
- git_filebuf file;
- unsigned int should_write:1;
-} git_indexwriter;
-
-#define GIT_INDEXWRITER_INIT { NULL, GIT_FILEBUF_INIT }
-
-/* Lock the index for eventual writing. */
-extern int git_indexwriter_init(git_indexwriter *writer, git_index *index);
-
-/* Lock the index for eventual writing by a repository operation: a merge,
- * revert, cherry-pick or a rebase. Note that the given checkout strategy
- * will be updated for the operation's use so that checkout will not write
- * the index.
- */
-extern int git_indexwriter_init_for_operation(
- git_indexwriter *writer,
- git_repository *repo,
- unsigned int *checkout_strategy);
-
-/* Write the index and unlock it. */
-extern int git_indexwriter_commit(git_indexwriter *writer);
-
-/* Cleanup an index writing session, unlocking the file (if it is still
- * locked and freeing any data structures.
- */
-extern void git_indexwriter_cleanup(git_indexwriter *writer);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/indexer.h"
-#include "git2/object.h"
-
-#include "common.h"
-#include "pack.h"
-#include "mwindow.h"
-#include "posix.h"
-#include "pack.h"
-#include "filebuf.h"
-#include "oid.h"
-#include "oidmap.h"
-#include "zstream.h"
-
-GIT__USE_OIDMAP
-
-extern git_mutex git__mwindow_mutex;
-
-#define UINT31_MAX (0x7FFFFFFF)
-
-struct entry {
- git_oid oid;
- uint32_t crc;
- uint32_t offset;
- uint64_t offset_long;
-};
-
-struct git_indexer {
- unsigned int parsed_header :1,
- opened_pack :1,
- have_stream :1,
- have_delta :1;
- struct git_pack_header hdr;
- struct git_pack_file *pack;
- unsigned int mode;
- git_off_t off;
- git_off_t entry_start;
- git_packfile_stream stream;
- size_t nr_objects;
- git_vector objects;
- git_vector deltas;
- unsigned int fanout[256];
- git_hash_ctx hash_ctx;
- git_oid hash;
- git_transfer_progress_cb progress_cb;
- void *progress_payload;
- char objbuf[8*1024];
-
- /* Needed to look up objects which we want to inject to fix a thin pack */
- git_odb *odb;
-
- /* Fields for calculating the packfile trailer (hash of everything before it) */
- char inbuf[GIT_OID_RAWSZ];
- size_t inbuf_len;
- git_hash_ctx trailer;
-};
-
-struct delta_info {
- git_off_t delta_off;
-};
-
-const git_oid *git_indexer_hash(const git_indexer *idx)
-{
- return &idx->hash;
-}
-
-static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
-{
- int error;
- git_map map;
-
- if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_SHARED, pack->mwf.fd, 0)) < 0)
- return error;
-
- memcpy(hdr, map.data, sizeof(*hdr));
- p_munmap(&map);
-
- /* Verify we recognize this pack file format. */
- if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
- giterr_set(GITERR_INDEXER, "Wrong pack signature");
- return -1;
- }
-
- if (!pack_version_ok(hdr->hdr_version)) {
- giterr_set(GITERR_INDEXER, "Wrong pack version");
- return -1;
- }
-
- return 0;
-}
-
-static int objects_cmp(const void *a, const void *b)
-{
- const struct entry *entrya = a;
- const struct entry *entryb = b;
-
- return git_oid__cmp(&entrya->oid, &entryb->oid);
-}
-
-int git_indexer_new(
- git_indexer **out,
- const char *prefix,
- unsigned int mode,
- git_odb *odb,
- git_transfer_progress_cb progress_cb,
- void *progress_payload)
-{
- git_indexer *idx;
- git_buf path = GIT_BUF_INIT, tmp_path = GIT_BUF_INIT;
- static const char suff[] = "/pack";
- int error, fd = -1;
-
- idx = git__calloc(1, sizeof(git_indexer));
- GITERR_CHECK_ALLOC(idx);
- idx->odb = odb;
- idx->progress_cb = progress_cb;
- idx->progress_payload = progress_payload;
- idx->mode = mode ? mode : GIT_PACK_FILE_MODE;
- git_hash_ctx_init(&idx->hash_ctx);
- git_hash_ctx_init(&idx->trailer);
-
- error = git_buf_joinpath(&path, prefix, suff);
- if (error < 0)
- goto cleanup;
-
- fd = git_futils_mktmp(&tmp_path, git_buf_cstr(&path), idx->mode);
- git_buf_free(&path);
- if (fd < 0)
- goto cleanup;
-
- error = git_packfile_alloc(&idx->pack, git_buf_cstr(&tmp_path));
- git_buf_free(&tmp_path);
-
- if (error < 0)
- goto cleanup;
-
- idx->pack->mwf.fd = fd;
- if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0)
- goto cleanup;
-
- *out = idx;
- return 0;
-
-cleanup:
- if (fd != -1)
- p_close(fd);
-
- git_buf_free(&path);
- git_buf_free(&tmp_path);
- git__free(idx);
- return -1;
-}
-
-/* Try to store the delta so we can try to resolve it later */
-static int store_delta(git_indexer *idx)
-{
- struct delta_info *delta;
-
- delta = git__calloc(1, sizeof(struct delta_info));
- GITERR_CHECK_ALLOC(delta);
- delta->delta_off = idx->entry_start;
-
- if (git_vector_insert(&idx->deltas, delta) < 0)
- return -1;
-
- return 0;
-}
-
-static void hash_header(git_hash_ctx *ctx, git_off_t len, git_otype type)
-{
- char buffer[64];
- size_t hdrlen;
-
- hdrlen = git_odb__format_object_header(buffer, sizeof(buffer), (size_t)len, type);
- git_hash_update(ctx, buffer, hdrlen);
-}
-
-static int hash_object_stream(git_indexer*idx, git_packfile_stream *stream)
-{
- ssize_t read;
-
- assert(idx && stream);
-
- do {
- if ((read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf))) < 0)
- break;
-
- git_hash_update(&idx->hash_ctx, idx->objbuf, read);
- } while (read > 0);
-
- if (read < 0)
- return (int)read;
-
- return 0;
-}
-
-/* In order to create the packfile stream, we need to skip over the delta base description */
-static int advance_delta_offset(git_indexer *idx, git_otype type)
-{
- git_mwindow *w = NULL;
-
- assert(type == GIT_OBJ_REF_DELTA || type == GIT_OBJ_OFS_DELTA);
-
- if (type == GIT_OBJ_REF_DELTA) {
- idx->off += GIT_OID_RAWSZ;
- } else {
- git_off_t base_off = get_delta_base(idx->pack, &w, &idx->off, type, idx->entry_start);
- git_mwindow_close(&w);
- if (base_off < 0)
- return (int)base_off;
- }
-
- return 0;
-}
-
-/* Read from the stream and discard any output */
-static int read_object_stream(git_indexer *idx, git_packfile_stream *stream)
-{
- ssize_t read;
-
- assert(stream);
-
- do {
- read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf));
- } while (read > 0);
-
- if (read < 0)
- return (int)read;
-
- return 0;
-}
-
-static int crc_object(uint32_t *crc_out, git_mwindow_file *mwf, git_off_t start, git_off_t size)
-{
- void *ptr;
- uint32_t crc;
- unsigned int left, len;
- git_mwindow *w = NULL;
-
- crc = crc32(0L, Z_NULL, 0);
- while (size) {
- ptr = git_mwindow_open(mwf, &w, start, (size_t)size, &left);
- if (ptr == NULL)
- return -1;
-
- len = min(left, (unsigned int)size);
- crc = crc32(crc, ptr, len);
- size -= len;
- start += len;
- git_mwindow_close(&w);
- }
-
- *crc_out = htonl(crc);
- return 0;
-}
-
-static int store_object(git_indexer *idx)
-{
- int i, error;
- khiter_t k;
- git_oid oid;
- struct entry *entry;
- git_off_t entry_size;
- struct git_pack_entry *pentry;
- git_off_t entry_start = idx->entry_start;
-
- entry = git__calloc(1, sizeof(*entry));
- GITERR_CHECK_ALLOC(entry);
-
- pentry = git__calloc(1, sizeof(struct git_pack_entry));
- GITERR_CHECK_ALLOC(pentry);
-
- git_hash_final(&oid, &idx->hash_ctx);
- entry_size = idx->off - entry_start;
- if (entry_start > UINT31_MAX) {
- entry->offset = UINT32_MAX;
- entry->offset_long = entry_start;
- } else {
- entry->offset = (uint32_t)entry_start;
- }
-
- git_oid_cpy(&pentry->sha1, &oid);
- pentry->offset = entry_start;
-
- k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
- if (error == -1) {
- git__free(pentry);
- giterr_set_oom();
- goto on_error;
- }
-
- if (error == 0) {
- giterr_set(GITERR_INDEXER, "duplicate object %s found in pack", git_oid_tostr_s(&pentry->sha1));
- git__free(pentry);
- goto on_error;
- }
-
-
- kh_value(idx->pack->idx_cache, k) = pentry;
-
- git_oid_cpy(&entry->oid, &oid);
-
- if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
- goto on_error;
-
- /* Add the object to the list */
- if (git_vector_insert(&idx->objects, entry) < 0)
- goto on_error;
-
- for (i = oid.id[0]; i < 256; ++i) {
- idx->fanout[i]++;
- }
-
- return 0;
-
-on_error:
- git__free(entry);
-
- return -1;
-}
-
-GIT_INLINE(bool) has_entry(git_indexer *idx, git_oid *id)
-{
- khiter_t k;
- k = kh_get(oid, idx->pack->idx_cache, id);
- return (k != kh_end(idx->pack->idx_cache));
-}
-
-static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_entry *pentry, git_off_t entry_start)
-{
- int i, error;
- khiter_t k;
-
- if (entry_start > UINT31_MAX) {
- entry->offset = UINT32_MAX;
- entry->offset_long = entry_start;
- } else {
- entry->offset = (uint32_t)entry_start;
- }
-
- pentry->offset = entry_start;
- k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
-
- if (error <= 0) {
- giterr_set(GITERR_INDEXER, "cannot insert object into pack");
- return -1;
- }
-
- kh_value(idx->pack->idx_cache, k) = pentry;
-
- /* Add the object to the list */
- if (git_vector_insert(&idx->objects, entry) < 0)
- return -1;
-
- for (i = entry->oid.id[0]; i < 256; ++i) {
- idx->fanout[i]++;
- }
-
- return 0;
-}
-
-static int hash_and_save(git_indexer *idx, git_rawobj *obj, git_off_t entry_start)
-{
- git_oid oid;
- size_t entry_size;
- struct entry *entry;
- struct git_pack_entry *pentry = NULL;
-
- entry = git__calloc(1, sizeof(*entry));
- GITERR_CHECK_ALLOC(entry);
-
- if (git_odb__hashobj(&oid, obj) < 0) {
- giterr_set(GITERR_INDEXER, "Failed to hash object");
- goto on_error;
- }
-
- pentry = git__calloc(1, sizeof(struct git_pack_entry));
- GITERR_CHECK_ALLOC(pentry);
-
- git_oid_cpy(&pentry->sha1, &oid);
- git_oid_cpy(&entry->oid, &oid);
- entry->crc = crc32(0L, Z_NULL, 0);
-
- entry_size = (size_t)(idx->off - entry_start);
- if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
- goto on_error;
-
- return save_entry(idx, entry, pentry, entry_start);
-
-on_error:
- git__free(pentry);
- git__free(entry);
- git__free(obj->data);
- return -1;
-}
-
-static int do_progress_callback(git_indexer *idx, git_transfer_progress *stats)
-{
- if (idx->progress_cb)
- return giterr_set_after_callback_function(
- idx->progress_cb(stats, idx->progress_payload),
- "indexer progress");
- return 0;
-}
-
-/* Hash everything but the last 20B of input */
-static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
-{
- size_t to_expell, to_keep;
-
- if (size == 0)
- return;
-
- /* Easy case, dump the buffer and the data minus the last 20 bytes */
- if (size >= GIT_OID_RAWSZ) {
- git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
- git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);
-
- data += size - GIT_OID_RAWSZ;
- memcpy(idx->inbuf, data, GIT_OID_RAWSZ);
- idx->inbuf_len = GIT_OID_RAWSZ;
- return;
- }
-
- /* We can just append */
- if (idx->inbuf_len + size <= GIT_OID_RAWSZ) {
- memcpy(idx->inbuf + idx->inbuf_len, data, size);
- idx->inbuf_len += size;
- return;
- }
-
- /* We need to partially drain the buffer and then append */
- to_keep = GIT_OID_RAWSZ - size;
- to_expell = idx->inbuf_len - to_keep;
-
- git_hash_update(&idx->trailer, idx->inbuf, to_expell);
-
- memmove(idx->inbuf, idx->inbuf + to_expell, to_keep);
- memcpy(idx->inbuf + to_keep, data, size);
- idx->inbuf_len += size - to_expell;
-}
-
-static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t size)
-{
- git_file fd = idx->pack->mwf.fd;
- size_t mmap_alignment;
- size_t page_offset;
- git_off_t page_start;
- unsigned char *map_data;
- git_map map;
- int error;
-
- assert(data && size);
-
- if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
- return error;
-
- /* the offset needs to be at the mmap boundary for the platform */
- page_offset = offset % mmap_alignment;
- page_start = offset - page_offset;
-
- if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0)
- return error;
-
- map_data = (unsigned char *)map.data;
- memcpy(map_data + page_offset, data, size);
- p_munmap(&map);
-
- return 0;
-}
-
-static int append_to_pack(git_indexer *idx, const void *data, size_t size)
-{
- git_off_t current_size = idx->pack->mwf.size;
- int fd = idx->pack->mwf.fd;
-
- if (!size)
- return 0;
-
- if (p_lseek(fd, current_size + size - 1, SEEK_SET) < 0 ||
- p_write(idx->pack->mwf.fd, data, 1) < 0) {
- giterr_set(GITERR_OS, "cannot extend packfile '%s'", idx->pack->pack_name);
- return -1;
- }
-
- return write_at(idx, data, idx->pack->mwf.size, size);
-}
-
-int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats)
-{
- int error = -1;
- size_t processed;
- struct git_pack_header *hdr = &idx->hdr;
- git_mwindow_file *mwf = &idx->pack->mwf;
-
- assert(idx && data && stats);
-
- processed = stats->indexed_objects;
-
- if ((error = append_to_pack(idx, data, size)) < 0)
- return error;
-
- hash_partially(idx, data, (int)size);
-
- /* Make sure we set the new size of the pack */
- idx->pack->mwf.size += size;
-
- if (!idx->parsed_header) {
- unsigned int total_objects;
-
- if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header))
- return 0;
-
- if ((error = parse_header(&idx->hdr, idx->pack)) < 0)
- return error;
-
- idx->parsed_header = 1;
- idx->nr_objects = ntohl(hdr->hdr_entries);
- idx->off = sizeof(struct git_pack_header);
-
- /* for now, limit to 2^32 objects */
- assert(idx->nr_objects == (size_t)((unsigned int)idx->nr_objects));
- if (idx->nr_objects == (size_t)((unsigned int)idx->nr_objects))
- total_objects = (unsigned int)idx->nr_objects;
- else
- total_objects = UINT_MAX;
-
- idx->pack->idx_cache = git_oidmap_alloc();
- GITERR_CHECK_ALLOC(idx->pack->idx_cache);
-
- idx->pack->has_cache = 1;
- if (git_vector_init(&idx->objects, total_objects, objects_cmp) < 0)
- return -1;
-
- if (git_vector_init(&idx->deltas, total_objects / 2, NULL) < 0)
- return -1;
-
- stats->received_objects = 0;
- stats->local_objects = 0;
- stats->total_deltas = 0;
- stats->indexed_deltas = 0;
- processed = stats->indexed_objects = 0;
- stats->total_objects = total_objects;
-
- if ((error = do_progress_callback(idx, stats)) != 0)
- return error;
- }
-
- /* Now that we have data in the pack, let's try to parse it */
-
- /* As the file grows any windows we try to use will be out of date */
- git_mwindow_free_all(mwf);
-
- while (processed < idx->nr_objects) {
- git_packfile_stream *stream = &idx->stream;
- git_off_t entry_start = idx->off;
- size_t entry_size;
- git_otype type;
- git_mwindow *w = NULL;
-
- if (idx->pack->mwf.size <= idx->off + 20)
- return 0;
-
- if (!idx->have_stream) {
- error = git_packfile_unpack_header(&entry_size, &type, mwf, &w, &idx->off);
- if (error == GIT_EBUFS) {
- idx->off = entry_start;
- return 0;
- }
- if (error < 0)
- goto on_error;
-
- git_mwindow_close(&w);
- idx->entry_start = entry_start;
- git_hash_init(&idx->hash_ctx);
-
- if (type == GIT_OBJ_REF_DELTA || type == GIT_OBJ_OFS_DELTA) {
- error = advance_delta_offset(idx, type);
- if (error == GIT_EBUFS) {
- idx->off = entry_start;
- return 0;
- }
- if (error < 0)
- goto on_error;
-
- idx->have_delta = 1;
- } else {
- idx->have_delta = 0;
- hash_header(&idx->hash_ctx, entry_size, type);
- }
-
- idx->have_stream = 1;
-
- error = git_packfile_stream_open(stream, idx->pack, idx->off);
- if (error < 0)
- goto on_error;
- }
-
- if (idx->have_delta) {
- error = read_object_stream(idx, stream);
- } else {
- error = hash_object_stream(idx, stream);
- }
-
- idx->off = stream->curpos;
- if (error == GIT_EBUFS)
- return 0;
-
- /* We want to free the stream reasorces no matter what here */
- idx->have_stream = 0;
- git_packfile_stream_free(stream);
-
- if (error < 0)
- goto on_error;
-
- if (idx->have_delta) {
- error = store_delta(idx);
- } else {
- error = store_object(idx);
- }
-
- if (error < 0)
- goto on_error;
-
- if (!idx->have_delta) {
- stats->indexed_objects = (unsigned int)++processed;
- }
- stats->received_objects++;
-
- if ((error = do_progress_callback(idx, stats)) != 0)
- goto on_error;
- }
-
- return 0;
-
-on_error:
- git_mwindow_free_all(mwf);
- return error;
-}
-
-static int index_path(git_buf *path, git_indexer *idx, const char *suffix)
-{
- const char prefix[] = "pack-";
- size_t slash = (size_t)path->size;
-
- /* search backwards for '/' */
- while (slash > 0 && path->ptr[slash - 1] != '/')
- slash--;
-
- if (git_buf_grow(path, slash + 1 + strlen(prefix) +
- GIT_OID_HEXSZ + strlen(suffix) + 1) < 0)
- return -1;
-
- git_buf_truncate(path, slash);
- git_buf_puts(path, prefix);
- git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash);
- path->size += GIT_OID_HEXSZ;
- git_buf_puts(path, suffix);
-
- return git_buf_oom(path) ? -1 : 0;
-}
-
-/**
- * Rewind the packfile by the trailer, as we might need to fix the
- * packfile by injecting objects at the tail and must overwrite it.
- */
-static void seek_back_trailer(git_indexer *idx)
-{
- idx->pack->mwf.size -= GIT_OID_RAWSZ;
- git_mwindow_free_all(&idx->pack->mwf);
-}
-
-static int inject_object(git_indexer *idx, git_oid *id)
-{
- git_odb_object *obj;
- struct entry *entry;
- struct git_pack_entry *pentry = NULL;
- git_oid foo = {{0}};
- unsigned char hdr[64];
- git_buf buf = GIT_BUF_INIT;
- git_off_t entry_start;
- const void *data;
- size_t len, hdr_len;
- int error;
-
- seek_back_trailer(idx);
- entry_start = idx->pack->mwf.size;
-
- if (git_odb_read(&obj, idx->odb, id) < 0) {
- giterr_set(GITERR_INDEXER, "missing delta bases");
- return -1;
- }
-
- data = git_odb_object_data(obj);
- len = git_odb_object_size(obj);
-
- entry = git__calloc(1, sizeof(*entry));
- GITERR_CHECK_ALLOC(entry);
-
- entry->crc = crc32(0L, Z_NULL, 0);
-
- /* Write out the object header */
- hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj));
- if ((error = append_to_pack(idx, hdr, hdr_len)) < 0)
- goto cleanup;
-
- idx->pack->mwf.size += hdr_len;
- entry->crc = crc32(entry->crc, hdr, (uInt)hdr_len);
-
- if ((error = git_zstream_deflatebuf(&buf, data, len)) < 0)
- goto cleanup;
-
- /* And then the compressed object */
- if ((error = append_to_pack(idx, buf.ptr, buf.size)) < 0)
- goto cleanup;
-
- idx->pack->mwf.size += buf.size;
- entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size));
- git_buf_free(&buf);
-
- /* Write a fake trailer so the pack functions play ball */
-
- if ((error = append_to_pack(idx, &foo, GIT_OID_RAWSZ)) < 0)
- goto cleanup;
-
- idx->pack->mwf.size += GIT_OID_RAWSZ;
-
- pentry = git__calloc(1, sizeof(struct git_pack_entry));
- GITERR_CHECK_ALLOC(pentry);
-
- git_oid_cpy(&pentry->sha1, id);
- git_oid_cpy(&entry->oid, id);
- idx->off = entry_start + hdr_len + len;
-
- error = save_entry(idx, entry, pentry, entry_start);
-
-cleanup:
- if (error) {
- git__free(entry);
- git__free(pentry);
- }
-
- git_odb_object_free(obj);
- return error;
-}
-
-static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
-{
- int error, found_ref_delta = 0;
- unsigned int i;
- struct delta_info *delta;
- size_t size;
- git_otype type;
- git_mwindow *w = NULL;
- git_off_t curpos = 0;
- unsigned char *base_info;
- unsigned int left = 0;
- git_oid base;
-
- assert(git_vector_length(&idx->deltas) > 0);
-
- if (idx->odb == NULL) {
- giterr_set(GITERR_INDEXER, "cannot fix a thin pack without an ODB");
- return -1;
- }
-
- /* Loop until we find the first REF delta */
- git_vector_foreach(&idx->deltas, i, delta) {
- if (!delta)
- continue;
-
- curpos = delta->delta_off;
- error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
- if (error < 0)
- return error;
-
- if (type == GIT_OBJ_REF_DELTA) {
- found_ref_delta = 1;
- break;
- }
- }
-
- if (!found_ref_delta) {
- giterr_set(GITERR_INDEXER, "no REF_DELTA found, cannot inject object");
- return -1;
- }
-
- /* curpos now points to the base information, which is an OID */
- base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, GIT_OID_RAWSZ, &left);
- if (base_info == NULL) {
- giterr_set(GITERR_INDEXER, "failed to map delta information");
- return -1;
- }
-
- git_oid_fromraw(&base, base_info);
- git_mwindow_close(&w);
-
- if (has_entry(idx, &base))
- return 0;
-
- if (inject_object(idx, &base) < 0)
- return -1;
-
- stats->local_objects++;
-
- return 0;
-}
-
-static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats)
-{
- unsigned int i;
- struct delta_info *delta;
- int progressed = 0, non_null = 0, progress_cb_result;
-
- while (idx->deltas.length > 0) {
- progressed = 0;
- non_null = 0;
- git_vector_foreach(&idx->deltas, i, delta) {
- git_rawobj obj = {NULL};
-
- if (!delta)
- continue;
-
- non_null = 1;
- idx->off = delta->delta_off;
- if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
- continue;
-
- if (hash_and_save(idx, &obj, delta->delta_off) < 0)
- continue;
-
- git__free(obj.data);
- stats->indexed_objects++;
- stats->indexed_deltas++;
- progressed = 1;
- if ((progress_cb_result = do_progress_callback(idx, stats)) < 0)
- return progress_cb_result;
-
- /* remove from the list */
- git_vector_set(NULL, &idx->deltas, i, NULL);
- git__free(delta);
- }
-
- /* if none were actually set, we're done */
- if (!non_null)
- break;
-
- if (!progressed && (fix_thin_pack(idx, stats) < 0)) {
- return -1;
- }
- }
-
- return 0;
-}
-
-static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *stats)
-{
- void *ptr;
- size_t chunk = 1024*1024;
- git_off_t hashed = 0;
- git_mwindow *w = NULL;
- git_mwindow_file *mwf;
- unsigned int left;
-
- mwf = &idx->pack->mwf;
-
- git_hash_init(&idx->trailer);
-
-
- /* Update the header to include the numer of local objects we injected */
- idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
- if (write_at(idx, &idx->hdr, 0, sizeof(struct git_pack_header)) < 0)
- return -1;
-
- /*
- * We now use the same technique as before to determine the
- * hash. We keep reading up to the end and let
- * hash_partially() keep the existing trailer out of the
- * calculation.
- */
- git_mwindow_free_all(mwf);
- idx->inbuf_len = 0;
- while (hashed < mwf->size) {
- ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
- if (ptr == NULL)
- return -1;
-
- hash_partially(idx, ptr, left);
- hashed += left;
-
- git_mwindow_close(&w);
- }
-
- return 0;
-}
-
-int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
-{
- git_mwindow *w = NULL;
- unsigned int i, long_offsets = 0, left;
- int error;
- struct git_pack_idx_header hdr;
- git_buf filename = GIT_BUF_INIT;
- struct entry *entry;
- git_oid trailer_hash, file_hash;
- git_hash_ctx ctx;
- git_filebuf index_file = {0};
- void *packfile_trailer;
-
- if (!idx->parsed_header) {
- giterr_set(GITERR_INDEXER, "incomplete pack header");
- return -1;
- }
-
- if (git_hash_ctx_init(&ctx) < 0)
- return -1;
-
- /* Test for this before resolve_deltas(), as it plays with idx->off */
- if (idx->off + 20 < idx->pack->mwf.size) {
- giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack");
- return -1;
- }
-
- packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
- if (packfile_trailer == NULL) {
- git_mwindow_close(&w);
- goto on_error;
- }
-
- /* Compare the packfile trailer as it was sent to us and what we calculated */
- git_oid_fromraw(&file_hash, packfile_trailer);
- git_mwindow_close(&w);
-
- git_hash_final(&trailer_hash, &idx->trailer);
- if (git_oid_cmp(&file_hash, &trailer_hash)) {
- giterr_set(GITERR_INDEXER, "packfile trailer mismatch");
- return -1;
- }
-
- /* Freeze the number of deltas */
- stats->total_deltas = stats->total_objects - stats->indexed_objects;
-
- if ((error = resolve_deltas(idx, stats)) < 0)
- return error;
-
- if (stats->indexed_objects != stats->total_objects) {
- giterr_set(GITERR_INDEXER, "early EOF");
- return -1;
- }
-
- if (stats->local_objects > 0) {
- if (update_header_and_rehash(idx, stats) < 0)
- return -1;
-
- git_hash_final(&trailer_hash, &idx->trailer);
- write_at(idx, &trailer_hash, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ);
- }
-
- git_vector_sort(&idx->objects);
-
- git_buf_sets(&filename, idx->pack->pack_name);
- git_buf_shorten(&filename, strlen("pack"));
- git_buf_puts(&filename, "idx");
- if (git_buf_oom(&filename))
- return -1;
-
- if (git_filebuf_open(&index_file, filename.ptr,
- GIT_FILEBUF_HASH_CONTENTS, idx->mode) < 0)
- goto on_error;
-
- /* Write out the header */
- hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
- hdr.idx_version = htonl(2);
- git_filebuf_write(&index_file, &hdr, sizeof(hdr));
-
- /* Write out the fanout table */
- for (i = 0; i < 256; ++i) {
- uint32_t n = htonl(idx->fanout[i]);
- git_filebuf_write(&index_file, &n, sizeof(n));
- }
-
- /* Write out the object names (SHA-1 hashes) */
- git_vector_foreach(&idx->objects, i, entry) {
- git_filebuf_write(&index_file, &entry->oid, sizeof(git_oid));
- git_hash_update(&ctx, &entry->oid, GIT_OID_RAWSZ);
- }
- git_hash_final(&idx->hash, &ctx);
-
- /* Write out the CRC32 values */
- git_vector_foreach(&idx->objects, i, entry) {
- git_filebuf_write(&index_file, &entry->crc, sizeof(uint32_t));
- }
-
- /* Write out the offsets */
- git_vector_foreach(&idx->objects, i, entry) {
- uint32_t n;
-
- if (entry->offset == UINT32_MAX)
- n = htonl(0x80000000 | long_offsets++);
- else
- n = htonl(entry->offset);
-
- git_filebuf_write(&index_file, &n, sizeof(uint32_t));
- }
-
- /* Write out the long offsets */
- git_vector_foreach(&idx->objects, i, entry) {
- uint32_t split[2];
-
- if (entry->offset != UINT32_MAX)
- continue;
-
- split[0] = htonl(entry->offset_long >> 32);
- split[1] = htonl(entry->offset_long & 0xffffffff);
-
- git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2);
- }
-
- /* Write out the packfile trailer to the index */
- if (git_filebuf_write(&index_file, &trailer_hash, GIT_OID_RAWSZ) < 0)
- goto on_error;
-
- /* Write out the hash of the idx */
- if (git_filebuf_hash(&trailer_hash, &index_file) < 0)
- goto on_error;
-
- git_filebuf_write(&index_file, &trailer_hash, sizeof(git_oid));
-
- /* Figure out what the final name should be */
- if (index_path(&filename, idx, ".idx") < 0)
- goto on_error;
-
- /* Commit file */
- if (git_filebuf_commit_at(&index_file, filename.ptr) < 0)
- goto on_error;
-
- git_mwindow_free_all(&idx->pack->mwf);
- /* We need to close the descriptor here so Windows doesn't choke on commit_at */
- if (p_close(idx->pack->mwf.fd) < 0) {
- giterr_set(GITERR_OS, "failed to close packfile");
- goto on_error;
- }
-
- idx->pack->mwf.fd = -1;
-
- if (index_path(&filename, idx, ".pack") < 0)
- goto on_error;
-
- /* And don't forget to rename the packfile to its new place. */
- p_rename(idx->pack->pack_name, git_buf_cstr(&filename));
-
- git_buf_free(&filename);
- git_hash_ctx_cleanup(&ctx);
- return 0;
-
-on_error:
- git_mwindow_free_all(&idx->pack->mwf);
- git_filebuf_cleanup(&index_file);
- git_buf_free(&filename);
- git_hash_ctx_cleanup(&ctx);
- return -1;
-}
-
-void git_indexer_free(git_indexer *idx)
-{
- if (idx == NULL)
- return;
-
- git_vector_free_deep(&idx->objects);
-
- if (idx->pack && idx->pack->idx_cache) {
- struct git_pack_entry *pentry;
- kh_foreach_value(
- idx->pack->idx_cache, pentry, { git__free(pentry); });
-
- git_oidmap_free(idx->pack->idx_cache);
- }
-
- git_vector_free_deep(&idx->deltas);
-
- if (!git_mutex_lock(&git__mwindow_mutex)) {
- git_packfile_free(idx->pack);
- git_mutex_unlock(&git__mwindow_mutex);
- }
-
- git_hash_ctx_cleanup(&idx->trailer);
- git_hash_ctx_cleanup(&idx->hash_ctx);
- git__free(idx);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_integer_h__
-#define INCLUDE_integer_h__
-
-/** @return true if p fits into the range of a size_t */
-GIT_INLINE(int) git__is_sizet(git_off_t p)
-{
- size_t r = (size_t)p;
- return p == (git_off_t)r;
-}
-
-/** @return true if p fits into the range of an ssize_t */
-GIT_INLINE(int) git__is_ssizet(size_t p)
-{
- ssize_t r = (ssize_t)p;
- return p == (size_t)r;
-}
-
-/** @return true if p fits into the range of a uint32_t */
-GIT_INLINE(int) git__is_uint32(size_t p)
-{
- uint32_t r = (uint32_t)p;
- return p == (size_t)r;
-}
-
-/** @return true if p fits into the range of an unsigned long */
-GIT_INLINE(int) git__is_ulong(git_off_t p)
-{
- unsigned long r = (unsigned long)p;
- return p == (git_off_t)r;
-}
-
-/** @return true if p fits into the range of an int */
-GIT_INLINE(int) git__is_int(long long p)
-{
- int r = (int)p;
- return p == (long long)r;
-}
-
-/**
- * Sets `one + two` into `out`, unless the arithmetic would overflow.
- * @return true if the result fits in a `uint64_t`, false on overflow.
- */
-GIT_INLINE(bool) git__add_uint64_overflow(uint64_t *out, uint64_t one, uint64_t two)
-{
- if (UINT64_MAX - one < two)
- return true;
- *out = one + two;
- return false;
-}
-
-/* Use clang/gcc compiler intrinsics whenever possible */
-#if (SIZE_MAX == UINT_MAX) && __has_builtin(__builtin_uadd_overflow)
-# define git__add_sizet_overflow(out, one, two) \
- __builtin_uadd_overflow(one, two, out)
-# define git__multiply_sizet_overflow(out, one, two) \
- __builtin_umul_overflow(one, two, out)
-#elif (SIZE_MAX == ULONG_MAX) && __has_builtin(__builtin_uaddl_overflow)
-# define git__add_sizet_overflow(out, one, two) \
- __builtin_uaddl_overflow(one, two, out)
-# define git__multiply_sizet_overflow(out, one, two) \
- __builtin_umull_overflow(one, two, out)
-#else
-
-/**
- * Sets `one + two` into `out`, unless the arithmetic would overflow.
- * @return true if the result fits in a `size_t`, false on overflow.
- */
-GIT_INLINE(bool) git__add_sizet_overflow(size_t *out, size_t one, size_t two)
-{
- if (SIZE_MAX - one < two)
- return true;
- *out = one + two;
- return false;
-}
-
-/**
- * Sets `one * two` into `out`, unless the arithmetic would overflow.
- * @return true if the result fits in a `size_t`, false on overflow.
- */
-GIT_INLINE(bool) git__multiply_sizet_overflow(size_t *out, size_t one, size_t two)
-{
- if (one && SIZE_MAX / one < two)
- return true;
- *out = one * two;
- return false;
-}
-
-#endif
-
-#endif /* INCLUDE_integer_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "iterator.h"
-#include "tree.h"
-#include "index.h"
-
-#define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
-#define GIT_ITERATOR_HONOR_IGNORES (1 << 16)
-#define GIT_ITERATOR_IGNORE_DOT_GIT (1 << 17)
-
-#define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
-#define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
-#define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
-#define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
-#define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
-#define iterator__include_conflicts(I) iterator__flag(I,INCLUDE_CONFLICTS)
-#define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
-#define iterator__honor_ignores(I) iterator__flag(I,HONOR_IGNORES)
-#define iterator__ignore_dot_git(I) iterator__flag(I,IGNORE_DOT_GIT)
-
-
-static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
-{
- if (ignore_case)
- iter->flags |= GIT_ITERATOR_IGNORE_CASE;
- else
- iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
-
- iter->strcomp = ignore_case ? git__strcasecmp : git__strcmp;
- iter->strncomp = ignore_case ? git__strncasecmp : git__strncmp;
- iter->prefixcomp = ignore_case ? git__prefixcmp_icase : git__prefixcmp;
- iter->entry_srch = ignore_case ? git_index_entry_isrch : git_index_entry_srch;
-
- git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp);
-}
-
-static int iterator_range_init(
- git_iterator *iter, const char *start, const char *end)
-{
- if (start && *start) {
- iter->start = git__strdup(start);
- GITERR_CHECK_ALLOC(iter->start);
-
- iter->start_len = strlen(iter->start);
- }
-
- if (end && *end) {
- iter->end = git__strdup(end);
- GITERR_CHECK_ALLOC(iter->end);
-
- iter->end_len = strlen(iter->end);
- }
-
- iter->started = (iter->start == NULL);
- iter->ended = false;
-
- return 0;
-}
-
-static void iterator_range_free(git_iterator *iter)
-{
- if (iter->start) {
- git__free(iter->start);
- iter->start = NULL;
- iter->start_len = 0;
- }
-
- if (iter->end) {
- git__free(iter->end);
- iter->end = NULL;
- iter->end_len = 0;
- }
-}
-
-static int iterator_reset_range(
- git_iterator *iter, const char *start, const char *end)
-{
- iterator_range_free(iter);
- return iterator_range_init(iter, start, end);
-}
-
-static int iterator_pathlist_init(git_iterator *iter, git_strarray *pathlist)
-{
- size_t i;
-
- if (git_vector_init(&iter->pathlist, pathlist->count, NULL) < 0)
- return -1;
-
- for (i = 0; i < pathlist->count; i++) {
- if (!pathlist->strings[i])
- continue;
-
- if (git_vector_insert(&iter->pathlist, pathlist->strings[i]) < 0)
- return -1;
- }
-
- return 0;
-}
-
-static int iterator_init_common(
- git_iterator *iter,
- git_repository *repo,
- git_index *index,
- git_iterator_options *given_opts)
-{
- static git_iterator_options default_opts = GIT_ITERATOR_OPTIONS_INIT;
- git_iterator_options *options = given_opts ? given_opts : &default_opts;
- bool ignore_case;
- int precompose;
- int error;
-
- iter->repo = repo;
- iter->index = index;
- iter->flags = options->flags;
-
- if ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) {
- ignore_case = true;
- } else if ((iter->flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0) {
- ignore_case = false;
- } else if (repo) {
- git_index *index;
-
- if ((error = git_repository_index__weakptr(&index, iter->repo)) < 0)
- return error;
-
- ignore_case = !!index->ignore_case;
-
- if (ignore_case == 1)
- iter->flags |= GIT_ITERATOR_IGNORE_CASE;
- else
- iter->flags |= GIT_ITERATOR_DONT_IGNORE_CASE;
- } else {
- ignore_case = false;
- }
-
- /* try to look up precompose and set flag if appropriate */
- if (repo &&
- (iter->flags & GIT_ITERATOR_PRECOMPOSE_UNICODE) == 0 &&
- (iter->flags & GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE) == 0) {
-
- if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
- giterr_clear();
- else if (precompose)
- iter->flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
- }
-
- if ((iter->flags & GIT_ITERATOR_DONT_AUTOEXPAND))
- iter->flags |= GIT_ITERATOR_INCLUDE_TREES;
-
- if ((error = iterator_range_init(iter, options->start, options->end)) < 0 ||
- (error = iterator_pathlist_init(iter, &options->pathlist)) < 0)
- return error;
-
- iterator_set_ignore_case(iter, ignore_case);
- return 0;
-}
-
-static void iterator_clear(git_iterator *iter)
-{
- iter->started = false;
- iter->ended = false;
- iter->stat_calls = 0;
- iter->pathlist_walk_idx = 0;
- iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
-}
-
-GIT_INLINE(bool) iterator_has_started(
- git_iterator *iter, const char *path, bool is_submodule)
-{
- size_t path_len;
-
- if (iter->start == NULL || iter->started == true)
- return true;
-
- /* the starting path is generally a prefix - we have started once we
- * are prefixed by this path
- */
- iter->started = (iter->prefixcomp(path, iter->start) >= 0);
-
- if (iter->started)
- return true;
-
- path_len = strlen(path);
-
- /* if, however, we are a submodule, then we support `start` being
- * suffixed with a `/` for crazy legacy reasons. match `submod`
- * with a start path of `submod/`.
- */
- if (is_submodule && iter->start_len && path_len == iter->start_len - 1 &&
- iter->start[iter->start_len-1] == '/')
- return true;
-
- /* if, however, our current path is a directory, and our starting path
- * is _beneath_ that directory, then recurse into the directory (even
- * though we have not yet "started")
- */
- if (path_len > 0 && path[path_len-1] == '/' &&
- iter->strncomp(path, iter->start, path_len) == 0)
- return true;
-
- return false;
-}
-
-GIT_INLINE(bool) iterator_has_ended(git_iterator *iter, const char *path)
-{
- if (iter->end == NULL)
- return false;
- else if (iter->ended)
- return true;
-
- iter->ended = (iter->prefixcomp(path, iter->end) > 0);
- return iter->ended;
-}
-
-/* walker for the index and tree iterator that allows it to walk the sorted
- * pathlist entries alongside sorted iterator entries.
- */
-static bool iterator_pathlist_next_is(git_iterator *iter, const char *path)
-{
- char *p;
- size_t path_len, p_len, cmp_len, i;
- int cmp;
-
- if (iter->pathlist.length == 0)
- return true;
-
- git_vector_sort(&iter->pathlist);
-
- path_len = strlen(path);
-
- /* for comparison, drop the trailing slash on the current '/' */
- if (path_len && path[path_len-1] == '/')
- path_len--;
-
- for (i = iter->pathlist_walk_idx; i < iter->pathlist.length; i++) {
- p = iter->pathlist.contents[i];
- p_len = strlen(p);
-
- if (p_len && p[p_len-1] == '/')
- p_len--;
-
- cmp_len = min(path_len, p_len);
-
- /* see if the pathlist entry is a prefix of this path */
- cmp = iter->strncomp(p, path, cmp_len);
-
- /* prefix match - see if there's an exact match, or if we were
- * given a path that matches the directory
- */
- if (cmp == 0) {
- /* if this pathlist entry is not suffixed with a '/' then
- * it matches a path that is a file or a directory.
- * (eg, pathlist = "foo" and path is "foo" or "foo/" or
- * "foo/something")
- */
- if (p[cmp_len] == '\0' &&
- (path[cmp_len] == '\0' || path[cmp_len] == '/'))
- return true;
-
- /* if this pathlist entry _is_ suffixed with a '/' then
- * it matches only paths that are directories.
- * (eg, pathlist = "foo/" and path is "foo/" or "foo/something")
- */
- if (p[cmp_len] == '/' && path[cmp_len] == '/')
- return true;
- }
-
- /* this pathlist entry sorts before the given path, try the next */
- else if (cmp < 0) {
- iter->pathlist_walk_idx++;
- continue;
- }
-
- /* this pathlist sorts after the given path, no match. */
- else if (cmp > 0) {
- break;
- }
- }
-
- return false;
-}
-
-typedef enum {
- ITERATOR_PATHLIST_NONE = 0,
- ITERATOR_PATHLIST_IS_FILE = 1,
- ITERATOR_PATHLIST_IS_DIR = 2,
- ITERATOR_PATHLIST_IS_PARENT = 3,
- ITERATOR_PATHLIST_FULL = 4,
-} iterator_pathlist_search_t;
-
-static iterator_pathlist_search_t iterator_pathlist_search(
- git_iterator *iter, const char *path, size_t path_len)
-{
- const char *p;
- size_t idx;
- int error;
-
- if (iter->pathlist.length == 0)
- return ITERATOR_PATHLIST_FULL;
-
- git_vector_sort(&iter->pathlist);
-
- error = git_vector_bsearch2(&idx, &iter->pathlist,
- (git_vector_cmp)iter->strcomp, path);
-
- /* the given path was found in the pathlist. since the pathlist only
- * matches directories when they're suffixed with a '/', analyze the
- * path string to determine whether it's a directory or not.
- */
- if (error == 0) {
- if (path_len && path[path_len-1] == '/')
- return ITERATOR_PATHLIST_IS_DIR;
-
- return ITERATOR_PATHLIST_IS_FILE;
- }
-
- /* at this point, the path we're examining may be a directory (though we
- * don't know that yet, since we're avoiding a stat unless it's necessary)
- * so walk the pathlist looking for the given path with a '/' after it,
- */
- while ((p = git_vector_get(&iter->pathlist, idx)) != NULL) {
- if (iter->prefixcomp(p, path) != 0)
- break;
-
- /* an exact match would have been matched by the bsearch above */
- assert(p[path_len]);
-
- /* is this a literal directory entry (eg `foo/`) or a file beneath */
- if (p[path_len] == '/') {
- return (p[path_len+1] == '\0') ?
- ITERATOR_PATHLIST_IS_DIR :
- ITERATOR_PATHLIST_IS_PARENT;
- }
-
- if (p[path_len] > '/')
- break;
-
- idx++;
- }
-
- return ITERATOR_PATHLIST_NONE;
-}
-
-/* Empty iterator */
-
-static int empty_iterator_noop(const git_index_entry **e, git_iterator *i)
-{
- GIT_UNUSED(i);
-
- if (e)
- *e = NULL;
-
- return GIT_ITEROVER;
-}
-
-static int empty_iterator_advance_over(
- const git_index_entry **e,
- git_iterator_status_t *s,
- git_iterator *i)
-{
- *s = GIT_ITERATOR_STATUS_EMPTY;
- return empty_iterator_noop(e, i);
-}
-
-static int empty_iterator_reset(git_iterator *i)
-{
- GIT_UNUSED(i);
- return 0;
-}
-
-static void empty_iterator_free(git_iterator *i)
-{
- GIT_UNUSED(i);
-}
-
-typedef struct {
- git_iterator base;
- git_iterator_callbacks cb;
-} empty_iterator;
-
-int git_iterator_for_nothing(
- git_iterator **out,
- git_iterator_options *options)
-{
- empty_iterator *iter;
-
- static git_iterator_callbacks callbacks = {
- empty_iterator_noop,
- empty_iterator_noop,
- empty_iterator_noop,
- empty_iterator_advance_over,
- empty_iterator_reset,
- empty_iterator_free
- };
-
- *out = NULL;
-
- iter = git__calloc(1, sizeof(empty_iterator));
- GITERR_CHECK_ALLOC(iter);
-
- iter->base.type = GIT_ITERATOR_TYPE_EMPTY;
- iter->base.cb = &callbacks;
- iter->base.flags = options->flags;
-
- *out = &iter->base;
- return 0;
-}
-
-/* Tree iterator */
-
-typedef struct {
- git_tree_entry *tree_entry;
- const char *parent_path;
-} tree_iterator_entry;
-
-typedef struct {
- git_tree *tree;
-
- /* path to this particular frame (folder) */
- git_buf path;
-
- /* a sorted list of the entries for this frame (folder), these are
- * actually pointers to the iterator's entry pool.
- */
- git_vector entries;
- tree_iterator_entry *current;
-
- size_t next_idx;
-
- /* on case insensitive iterations, we also have an array of other
- * paths that were case insensitively equal to this one, and their
- * tree objects. we have coalesced the tree entries into this frame.
- * a child `tree_iterator_entry` will contain a pointer to its actual
- * parent path.
- */
- git_vector similar_trees;
- git_array_t(git_buf) similar_paths;
-} tree_iterator_frame;
-
-typedef struct {
- git_iterator base;
- git_tree *root;
- git_array_t(tree_iterator_frame) frames;
-
- git_index_entry entry;
- git_buf entry_path;
-
- /* a pool of entries to reduce the number of allocations */
- git_pool entry_pool;
-} tree_iterator;
-
-GIT_INLINE(tree_iterator_frame *) tree_iterator_parent_frame(
- tree_iterator *iter)
-{
- return iter->frames.size > 1 ?
- &iter->frames.ptr[iter->frames.size-2] : NULL;
-}
-
-GIT_INLINE(tree_iterator_frame *) tree_iterator_current_frame(
- tree_iterator *iter)
-{
- return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL;
-}
-
-GIT_INLINE(int) tree_entry_cmp(
- const git_tree_entry *a, const git_tree_entry *b, bool icase)
-{
- return git_path_cmp(
- a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE,
- b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE,
- icase ? git__strncasecmp : git__strncmp);
-}
-
-GIT_INLINE(int) tree_iterator_entry_cmp(const void *ptr_a, const void *ptr_b)
-{
- const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
- const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
-
- return tree_entry_cmp(a->tree_entry, b->tree_entry, false);
-}
-
-GIT_INLINE(int) tree_iterator_entry_cmp_icase(
- const void *ptr_a, const void *ptr_b)
-{
- const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
- const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
-
- return tree_entry_cmp(a->tree_entry, b->tree_entry, true);
-}
-
-static int tree_iterator_entry_sort_icase(const void *ptr_a, const void *ptr_b)
-{
- const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
- const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
-
- int c = tree_entry_cmp(a->tree_entry, b->tree_entry, true);
-
- /* stabilize the sort order for filenames that are (case insensitively)
- * the same by examining the parent path (case sensitively) before
- * falling back to a case sensitive sort of the filename.
- */
- if (!c && a->parent_path != b->parent_path)
- c = git__strcmp(a->parent_path, b->parent_path);
-
- if (!c)
- c = tree_entry_cmp(a->tree_entry, b->tree_entry, false);
-
- return c;
-}
-
-static int tree_iterator_compute_path(
- git_buf *out,
- tree_iterator_entry *entry)
-{
- git_buf_clear(out);
-
- if (entry->parent_path)
- git_buf_joinpath(out, entry->parent_path, entry->tree_entry->filename);
- else
- git_buf_puts(out, entry->tree_entry->filename);
-
- if (git_tree_entry__is_tree(entry->tree_entry))
- git_buf_putc(out, '/');
-
- if (git_buf_oom(out))
- return -1;
-
- return 0;
-}
-
-static int tree_iterator_frame_init(
- tree_iterator *iter,
- git_tree *tree,
- tree_iterator_entry *frame_entry)
-{
- tree_iterator_frame *new_frame = NULL;
- tree_iterator_entry *new_entry;
- git_tree *dup = NULL;
- git_tree_entry *tree_entry;
- git_vector_cmp cmp;
- size_t i;
- int error = 0;
-
- new_frame = git_array_alloc(iter->frames);
- GITERR_CHECK_ALLOC(new_frame);
-
- memset(new_frame, 0, sizeof(tree_iterator_frame));
-
- if ((error = git_tree_dup(&dup, tree)) < 0)
- goto done;
-
- memset(new_frame, 0x0, sizeof(tree_iterator_frame));
- new_frame->tree = dup;
-
- if (frame_entry &&
- (error = tree_iterator_compute_path(&new_frame->path, frame_entry)) < 0)
- goto done;
-
- cmp = iterator__ignore_case(&iter->base) ?
- tree_iterator_entry_sort_icase : NULL;
-
- if ((error = git_vector_init(
- &new_frame->entries, dup->entries.size, cmp)) < 0)
- goto done;
-
- git_array_foreach(dup->entries, i, tree_entry) {
- new_entry = git_pool_malloc(&iter->entry_pool, 1);
- GITERR_CHECK_ALLOC(new_entry);
-
- new_entry->tree_entry = tree_entry;
- new_entry->parent_path = new_frame->path.ptr;
-
- if ((error = git_vector_insert(&new_frame->entries, new_entry)) < 0)
- goto done;
- }
-
- git_vector_set_sorted(&new_frame->entries,
- !iterator__ignore_case(&iter->base));
-
-done:
- if (error < 0) {
- git_tree_free(dup);
- git_array_pop(iter->frames);
- }
-
- return error;
-}
-
-GIT_INLINE(tree_iterator_entry *) tree_iterator_current_entry(
- tree_iterator_frame *frame)
-{
- return frame->current;
-}
-
-GIT_INLINE(int) tree_iterator_frame_push_neighbors(
- tree_iterator *iter,
- tree_iterator_frame *parent_frame,
- tree_iterator_frame *frame,
- const char *filename)
-{
- tree_iterator_entry *entry, *new_entry;
- git_tree *tree = NULL;
- git_tree_entry *tree_entry;
- git_buf *path;
- size_t new_size, i;
- int error = 0;
-
- while (parent_frame->next_idx < parent_frame->entries.length) {
- entry = parent_frame->entries.contents[parent_frame->next_idx];
-
- if (strcasecmp(filename, entry->tree_entry->filename) != 0)
- break;
-
- if ((error = git_tree_lookup(&tree,
- iter->base.repo, entry->tree_entry->oid)) < 0)
- break;
-
- if (git_vector_insert(&parent_frame->similar_trees, tree) < 0)
- break;
-
- path = git_array_alloc(parent_frame->similar_paths);
- GITERR_CHECK_ALLOC(path);
-
- memset(path, 0, sizeof(git_buf));
-
- if ((error = tree_iterator_compute_path(path, entry)) < 0)
- break;
-
- GITERR_CHECK_ALLOC_ADD(&new_size,
- frame->entries.length, tree->entries.size);
- git_vector_size_hint(&frame->entries, new_size);
-
- git_array_foreach(tree->entries, i, tree_entry) {
- new_entry = git_pool_malloc(&iter->entry_pool, 1);
- GITERR_CHECK_ALLOC(new_entry);
-
- new_entry->tree_entry = tree_entry;
- new_entry->parent_path = path->ptr;
-
- if ((error = git_vector_insert(&frame->entries, new_entry)) < 0)
- break;
- }
-
- if (error)
- break;
-
- parent_frame->next_idx++;
- }
-
- return error;
-}
-
-GIT_INLINE(int) tree_iterator_frame_push(
- tree_iterator *iter, tree_iterator_entry *entry)
-{
- tree_iterator_frame *parent_frame, *frame;
- git_tree *tree = NULL;
- int error;
-
- if ((error = git_tree_lookup(&tree,
- iter->base.repo, entry->tree_entry->oid)) < 0 ||
- (error = tree_iterator_frame_init(iter, tree, entry)) < 0)
- goto done;
-
- parent_frame = tree_iterator_parent_frame(iter);
- frame = tree_iterator_current_frame(iter);
-
- /* if we're case insensitive, then we may have another directory that
- * is (case insensitively) equal to this one. coalesce those children
- * into this tree.
- */
- if (iterator__ignore_case(&iter->base))
- error = tree_iterator_frame_push_neighbors(iter,
- parent_frame, frame, entry->tree_entry->filename);
-
-done:
- git_tree_free(tree);
- return error;
-}
-
-static void tree_iterator_frame_pop(tree_iterator *iter)
-{
- tree_iterator_frame *frame;
- git_buf *buf = NULL;
- git_tree *tree;
- size_t i;
-
- assert(iter->frames.size);
-
- frame = git_array_pop(iter->frames);
-
- git_vector_free(&frame->entries);
- git_tree_free(frame->tree);
-
- do {
- buf = git_array_pop(frame->similar_paths);
- git_buf_free(buf);
- } while (buf != NULL);
-
- git_array_clear(frame->similar_paths);
-
- git_vector_foreach(&frame->similar_trees, i, tree)
- git_tree_free(tree);
-
- git_vector_free(&frame->similar_trees);
-
- git_buf_free(&frame->path);
-}
-
-static int tree_iterator_current(
- const git_index_entry **out, git_iterator *i)
-{
- tree_iterator *iter = (tree_iterator *)i;
-
- if (!iterator__has_been_accessed(i))
- return iter->base.cb->advance(out, i);
-
- if (!iter->frames.size) {
- *out = NULL;
- return GIT_ITEROVER;
- }
-
- *out = &iter->entry;
- return 0;
-}
-
-static void tree_iterator_set_current(
- tree_iterator *iter,
- tree_iterator_frame *frame,
- tree_iterator_entry *entry)
-{
- git_tree_entry *tree_entry = entry->tree_entry;
-
- frame->current = entry;
-
- memset(&iter->entry, 0x0, sizeof(git_index_entry));
-
- iter->entry.mode = tree_entry->attr;
- iter->entry.path = iter->entry_path.ptr;
- git_oid_cpy(&iter->entry.id, tree_entry->oid);
-}
-
-static int tree_iterator_advance(const git_index_entry **out, git_iterator *i)
-{
- tree_iterator *iter = (tree_iterator *)i;
- int error = 0;
-
- iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
-
- /* examine tree entries until we find the next one to return */
- while (true) {
- tree_iterator_entry *prev_entry, *entry;
- tree_iterator_frame *frame;
- bool is_tree;
-
- if ((frame = tree_iterator_current_frame(iter)) == NULL) {
- error = GIT_ITEROVER;
- break;
- }
-
- /* no more entries in this frame. pop the frame out */
- if (frame->next_idx == frame->entries.length) {
- tree_iterator_frame_pop(iter);
- continue;
- }
-
- /* we may have coalesced the contents of case-insensitively same-named
- * directories, so do the sort now.
- */
- if (frame->next_idx == 0 && !git_vector_is_sorted(&frame->entries))
- git_vector_sort(&frame->entries);
-
- /* we have more entries in the current frame, that's our next entry */
- prev_entry = tree_iterator_current_entry(frame);
- entry = frame->entries.contents[frame->next_idx];
- frame->next_idx++;
-
- /* we can have collisions when iterating case insensitively. (eg,
- * 'A/a' and 'a/A'). squash this one if it's already been seen.
- */
- if (iterator__ignore_case(&iter->base) &&
- prev_entry &&
- tree_iterator_entry_cmp_icase(prev_entry, entry) == 0)
- continue;
-
- if ((error = tree_iterator_compute_path(&iter->entry_path, entry)) < 0)
- break;
-
- /* if this path is before our start, advance over this entry */
- if (!iterator_has_started(&iter->base, iter->entry_path.ptr, false))
- continue;
-
- /* if this path is after our end, stop */
- if (iterator_has_ended(&iter->base, iter->entry_path.ptr)) {
- error = GIT_ITEROVER;
- break;
- }
-
- /* if we have a list of paths we're interested in, examine it */
- if (!iterator_pathlist_next_is(&iter->base, iter->entry_path.ptr))
- continue;
-
- is_tree = git_tree_entry__is_tree(entry->tree_entry);
-
- /* if we are *not* including trees then advance over this entry */
- if (is_tree && !iterator__include_trees(iter)) {
-
- /* if we've found a tree (and are not returning it to the caller)
- * and we are autoexpanding, then we want to return the first
- * child. push the new directory and advance.
- */
- if (iterator__do_autoexpand(iter)) {
- if ((error = tree_iterator_frame_push(iter, entry)) < 0)
- break;
- }
-
- continue;
- }
-
- tree_iterator_set_current(iter, frame, entry);
-
- /* if we are autoexpanding, then push this as a new frame, so that
- * the next call to `advance` will dive into this directory.
- */
- if (is_tree && iterator__do_autoexpand(iter))
- error = tree_iterator_frame_push(iter, entry);
-
- break;
- }
-
- if (out)
- *out = (error == 0) ? &iter->entry : NULL;
-
- return error;
-}
-
-static int tree_iterator_advance_into(
- const git_index_entry **out, git_iterator *i)
-{
- tree_iterator *iter = (tree_iterator *)i;
- tree_iterator_frame *frame;
- tree_iterator_entry *prev_entry;
- int error;
-
- if (out)
- *out = NULL;
-
- if ((frame = tree_iterator_current_frame(iter)) == NULL)
- return GIT_ITEROVER;
-
- /* get the last seen entry */
- prev_entry = tree_iterator_current_entry(frame);
-
- /* it's legal to call advance_into when auto-expand is on. in this case,
- * we will have pushed a new (empty) frame on to the stack for this
- * new directory. since it's empty, its current_entry should be null.
- */
- assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
-
- if (prev_entry) {
- if (!git_tree_entry__is_tree(prev_entry->tree_entry))
- return 0;
-
- if ((error = tree_iterator_frame_push(iter, prev_entry)) < 0)
- return error;
- }
-
- /* we've advanced into the directory in question, let advance
- * find the first entry
- */
- return tree_iterator_advance(out, i);
-}
-
-static int tree_iterator_advance_over(
- const git_index_entry **out,
- git_iterator_status_t *status,
- git_iterator *i)
-{
- *status = GIT_ITERATOR_STATUS_NORMAL;
- return git_iterator_advance(out, i);
-}
-
-static void tree_iterator_clear(tree_iterator *iter)
-{
- while (iter->frames.size)
- tree_iterator_frame_pop(iter);
-
- git_array_clear(iter->frames);
-
- git_pool_clear(&iter->entry_pool);
- git_buf_clear(&iter->entry_path);
-
- iterator_clear(&iter->base);
-}
-
-static int tree_iterator_init(tree_iterator *iter)
-{
- int error;
-
- git_pool_init(&iter->entry_pool, sizeof(tree_iterator_entry));
-
- if ((error = tree_iterator_frame_init(iter, iter->root, NULL)) < 0)
- return error;
-
- iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
-
- return 0;
-}
-
-static int tree_iterator_reset(git_iterator *i)
-{
- tree_iterator *iter = (tree_iterator *)i;
-
- tree_iterator_clear(iter);
- return tree_iterator_init(iter);
-}
-
-static void tree_iterator_free(git_iterator *i)
-{
- tree_iterator *iter = (tree_iterator *)i;
-
- tree_iterator_clear(iter);
-
- git_tree_free(iter->root);
- git_buf_free(&iter->entry_path);
-}
-
-int git_iterator_for_tree(
- git_iterator **out,
- git_tree *tree,
- git_iterator_options *options)
-{
- tree_iterator *iter;
- int error;
-
- static git_iterator_callbacks callbacks = {
- tree_iterator_current,
- tree_iterator_advance,
- tree_iterator_advance_into,
- tree_iterator_advance_over,
- tree_iterator_reset,
- tree_iterator_free
- };
-
- *out = NULL;
-
- if (tree == NULL)
- return git_iterator_for_nothing(out, options);
-
- iter = git__calloc(1, sizeof(tree_iterator));
- GITERR_CHECK_ALLOC(iter);
-
- iter->base.type = GIT_ITERATOR_TYPE_TREE;
- iter->base.cb = &callbacks;
-
- if ((error = iterator_init_common(&iter->base,
- git_tree_owner(tree), NULL, options)) < 0 ||
- (error = git_tree_dup(&iter->root, tree)) < 0 ||
- (error = tree_iterator_init(iter)) < 0)
- goto on_error;
-
- *out = &iter->base;
- return 0;
-
-on_error:
- git_iterator_free(&iter->base);
- return error;
-}
-
-int git_iterator_current_tree_entry(
- const git_tree_entry **tree_entry, git_iterator *i)
-{
- tree_iterator *iter;
- tree_iterator_frame *frame;
- tree_iterator_entry *entry;
-
- assert(i->type == GIT_ITERATOR_TYPE_TREE);
-
- iter = (tree_iterator *)i;
-
- frame = tree_iterator_current_frame(iter);
- entry = tree_iterator_current_entry(frame);
-
- *tree_entry = entry->tree_entry;
- return 0;
-}
-
-int git_iterator_current_parent_tree(
- const git_tree **parent_tree, git_iterator *i, size_t depth)
-{
- tree_iterator *iter;
- tree_iterator_frame *frame;
-
- assert(i->type == GIT_ITERATOR_TYPE_TREE);
-
- iter = (tree_iterator *)i;
-
- assert(depth < iter->frames.size);
- frame = &iter->frames.ptr[iter->frames.size-depth-1];
-
- *parent_tree = frame->tree;
- return 0;
-}
-
-/* Filesystem iterator */
-
-typedef struct {
- struct stat st;
- size_t path_len;
- iterator_pathlist_search_t match;
- char path[GIT_FLEX_ARRAY];
-} filesystem_iterator_entry;
-
-typedef struct {
- git_vector entries;
- git_pool entry_pool;
- size_t next_idx;
-
- size_t path_len;
- int is_ignored;
-} filesystem_iterator_frame;
-
-typedef struct {
- git_iterator base;
- char *root;
- size_t root_len;
-
- unsigned int dirload_flags;
-
- git_tree *tree;
- git_index *index;
- git_vector index_snapshot;
-
- git_array_t(filesystem_iterator_frame) frames;
- git_ignores ignores;
-
- /* info about the current entry */
- git_index_entry entry;
- git_buf current_path;
- int current_is_ignored;
-
- /* temporary buffer for advance_over */
- git_buf tmp_buf;
-} filesystem_iterator;
-
-
-GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_parent_frame(
- filesystem_iterator *iter)
-{
- return iter->frames.size > 1 ?
- &iter->frames.ptr[iter->frames.size-2] : NULL;
-}
-
-GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_current_frame(
- filesystem_iterator *iter)
-{
- return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL;
-}
-
-GIT_INLINE(filesystem_iterator_entry *) filesystem_iterator_current_entry(
- filesystem_iterator_frame *frame)
-{
- return frame->next_idx == 0 ?
- NULL : frame->entries.contents[frame->next_idx-1];
-}
-
-static int filesystem_iterator_entry_cmp(const void *_a, const void *_b)
-{
- const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a;
- const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b;
-
- return git__strcmp(a->path, b->path);
-}
-
-static int filesystem_iterator_entry_cmp_icase(const void *_a, const void *_b)
-{
- const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a;
- const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b;
-
- return git__strcasecmp(a->path, b->path);
-}
-
-#define FILESYSTEM_MAX_DEPTH 100
-
-/**
- * Figure out if an entry is a submodule.
- *
- * We consider it a submodule if the path is listed as a submodule in
- * either the tree or the index.
- */
-static int filesystem_iterator_is_submodule(
- bool *out, filesystem_iterator *iter, const char *path, size_t path_len)
-{
- bool is_submodule = false;
- int error;
-
- *out = false;
-
- /* first see if this path is a submodule in HEAD */
- if (iter->tree) {
- git_tree_entry *entry;
-
- error = git_tree_entry_bypath(&entry, iter->tree, path);
-
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
-
- if (!error) {
- is_submodule = (entry->attr == GIT_FILEMODE_COMMIT);
- git_tree_entry_free(entry);
- }
- }
-
- if (!is_submodule && iter->base.index) {
- size_t pos;
-
- error = git_index_snapshot_find(&pos,
- &iter->index_snapshot, iter->base.entry_srch, path, path_len, 0);
-
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
-
- if (!error) {
- git_index_entry *e = git_vector_get(&iter->index_snapshot, pos);
- is_submodule = (e->mode == GIT_FILEMODE_COMMIT);
- }
- }
-
- *out = is_submodule;
- return 0;
-}
-
-static void filesystem_iterator_frame_push_ignores(
- filesystem_iterator *iter,
- filesystem_iterator_entry *frame_entry,
- filesystem_iterator_frame *new_frame)
-{
- filesystem_iterator_frame *previous_frame;
- const char *path = frame_entry ? frame_entry->path : "";
-
- if (!iterator__honor_ignores(&iter->base))
- return;
-
- if (git_ignore__lookup(&new_frame->is_ignored,
- &iter->ignores, path, GIT_DIR_FLAG_TRUE) < 0) {
- giterr_clear();
- new_frame->is_ignored = GIT_IGNORE_NOTFOUND;
- }
-
- /* if this is not the top level directory... */
- if (frame_entry) {
- const char *relative_path;
-
- previous_frame = filesystem_iterator_parent_frame(iter);
-
- /* push new ignores for files in this directory */
- relative_path = frame_entry->path + previous_frame->path_len;
-
- /* inherit ignored from parent if no rule specified */
- if (new_frame->is_ignored <= GIT_IGNORE_NOTFOUND)
- new_frame->is_ignored = previous_frame->is_ignored;
-
- git_ignore__push_dir(&iter->ignores, relative_path);
- }
-}
-
-static void filesystem_iterator_frame_pop_ignores(
- filesystem_iterator *iter)
-{
- if (iterator__honor_ignores(&iter->base))
- git_ignore__pop_dir(&iter->ignores);
-}
-
-GIT_INLINE(bool) filesystem_iterator_examine_path(
- bool *is_dir_out,
- iterator_pathlist_search_t *match_out,
- filesystem_iterator *iter,
- filesystem_iterator_entry *frame_entry,
- const char *path,
- size_t path_len)
-{
- bool is_dir = 0;
- iterator_pathlist_search_t match = ITERATOR_PATHLIST_FULL;
-
- *is_dir_out = false;
- *match_out = ITERATOR_PATHLIST_NONE;
-
- if (iter->base.start_len) {
- int cmp = iter->base.strncomp(path, iter->base.start, path_len);
-
- /* we haven't stat'ed `path` yet, so we don't yet know if it's a
- * directory or not. special case if the current path may be a
- * directory that matches the start prefix.
- */
- if (cmp == 0) {
- if (iter->base.start[path_len] == '/')
- is_dir = true;
-
- else if (iter->base.start[path_len] != '\0')
- cmp = -1;
- }
-
- if (cmp < 0)
- return false;
- }
-
- if (iter->base.end_len) {
- int cmp = iter->base.strncomp(path, iter->base.end, iter->base.end_len);
-
- if (cmp > 0)
- return false;
- }
-
- /* if we have a pathlist that we're limiting to, examine this path now
- * to avoid a `stat` if we're not interested in the path.
- */
- if (iter->base.pathlist.length) {
- /* if our parent was explicitly included, so too are we */
- if (frame_entry && frame_entry->match != ITERATOR_PATHLIST_IS_PARENT)
- match = ITERATOR_PATHLIST_FULL;
- else
- match = iterator_pathlist_search(&iter->base, path, path_len);
-
- if (match == ITERATOR_PATHLIST_NONE)
- return false;
-
- /* Ensure that the pathlist entry lines up with what we expected */
- if (match == ITERATOR_PATHLIST_IS_DIR ||
- match == ITERATOR_PATHLIST_IS_PARENT)
- is_dir = true;
- }
-
- *is_dir_out = is_dir;
- *match_out = match;
- return true;
-}
-
-GIT_INLINE(bool) filesystem_iterator_is_dot_git(
- filesystem_iterator *iter, const char *path, size_t path_len)
-{
- size_t len;
-
- if (!iterator__ignore_dot_git(&iter->base))
- return false;
-
- if ((len = path_len) < 4)
- return false;
-
- if (path[len - 1] == '/')
- len--;
-
- if (git__tolower(path[len - 1]) != 't' ||
- git__tolower(path[len - 2]) != 'i' ||
- git__tolower(path[len - 3]) != 'g' ||
- git__tolower(path[len - 4]) != '.')
- return false;
-
- return (len == 4 || path[len - 5] == '/');
-}
-
-static filesystem_iterator_entry *filesystem_iterator_entry_init(
- filesystem_iterator_frame *frame,
- const char *path,
- size_t path_len,
- struct stat *statbuf,
- iterator_pathlist_search_t pathlist_match)
-{
- filesystem_iterator_entry *entry;
- size_t entry_size;
-
- /* Make sure to append two bytes, one for the path's null
- * termination, one for a possible trailing '/' for folders.
- */
- if (GIT_ADD_SIZET_OVERFLOW(&entry_size,
- sizeof(filesystem_iterator_entry), path_len) ||
- GIT_ADD_SIZET_OVERFLOW(&entry_size, entry_size, 2) ||
- (entry = git_pool_malloc(&frame->entry_pool, entry_size)) == NULL)
- return NULL;
-
- entry->path_len = path_len;
- entry->match = pathlist_match;
- memcpy(entry->path, path, path_len);
- memcpy(&entry->st, statbuf, sizeof(struct stat));
-
- /* Suffix directory paths with a '/' */
- if (S_ISDIR(entry->st.st_mode))
- entry->path[entry->path_len++] = '/';
-
- entry->path[entry->path_len] = '\0';
-
- return entry;
-}
-
-static int filesystem_iterator_frame_push(
- filesystem_iterator *iter,
- filesystem_iterator_entry *frame_entry)
-{
- filesystem_iterator_frame *new_frame = NULL;
- git_path_diriter diriter = GIT_PATH_DIRITER_INIT;
- git_buf root = GIT_BUF_INIT;
- const char *path;
- filesystem_iterator_entry *entry;
- struct stat statbuf;
- size_t path_len;
- int error;
-
- if (iter->frames.size == FILESYSTEM_MAX_DEPTH) {
- giterr_set(GITERR_REPOSITORY,
- "directory nesting too deep (%"PRIuZ")", iter->frames.size);
- return -1;
- }
-
- new_frame = git_array_alloc(iter->frames);
- GITERR_CHECK_ALLOC(new_frame);
-
- memset(new_frame, 0, sizeof(filesystem_iterator_frame));
-
- if (frame_entry)
- git_buf_joinpath(&root, iter->root, frame_entry->path);
- else
- git_buf_puts(&root, iter->root);
-
- if (git_buf_oom(&root)) {
- error = -1;
- goto done;
- }
-
- new_frame->path_len = frame_entry ? frame_entry->path_len : 0;
-
- /* Any error here is equivalent to the dir not existing, skip over it */
- if ((error = git_path_diriter_init(
- &diriter, root.ptr, iter->dirload_flags)) < 0) {
- error = GIT_ENOTFOUND;
- goto done;
- }
-
- if ((error = git_vector_init(&new_frame->entries, 64,
- iterator__ignore_case(&iter->base) ?
- filesystem_iterator_entry_cmp_icase :
- filesystem_iterator_entry_cmp)) < 0)
- goto done;
-
- git_pool_init(&new_frame->entry_pool, 1);
-
- /* check if this directory is ignored */
- filesystem_iterator_frame_push_ignores(iter, frame_entry, new_frame);
-
- while ((error = git_path_diriter_next(&diriter)) == 0) {
- iterator_pathlist_search_t pathlist_match = ITERATOR_PATHLIST_FULL;
- bool dir_expected = false;
-
- if ((error = git_path_diriter_fullpath(&path, &path_len, &diriter)) < 0)
- goto done;
-
- assert(path_len > iter->root_len);
-
- /* remove the prefix if requested */
- path += iter->root_len;
- path_len -= iter->root_len;
-
- /* examine start / end and the pathlist to see if this path is in it.
- * note that since we haven't yet stat'ed the path, we cannot know
- * whether it's a directory yet or not, so this can give us an
- * expected type (S_IFDIR or S_IFREG) that we should examine)
- */
- if (!filesystem_iterator_examine_path(&dir_expected, &pathlist_match,
- iter, frame_entry, path, path_len))
- continue;
-
- /* TODO: don't need to stat if assume unchanged for this path and
- * we have an index, we can just copy the data out of it.
- */
-
- if ((error = git_path_diriter_stat(&statbuf, &diriter)) < 0) {
- /* file was removed between readdir and lstat */
- if (error == GIT_ENOTFOUND)
- continue;
-
- /* treat the file as unreadable */
- memset(&statbuf, 0, sizeof(statbuf));
- statbuf.st_mode = GIT_FILEMODE_UNREADABLE;
-
- error = 0;
- }
-
- iter->base.stat_calls++;
-
- /* Ignore wacky things in the filesystem */
- if (!S_ISDIR(statbuf.st_mode) &&
- !S_ISREG(statbuf.st_mode) &&
- !S_ISLNK(statbuf.st_mode) &&
- statbuf.st_mode != GIT_FILEMODE_UNREADABLE)
- continue;
-
- if (filesystem_iterator_is_dot_git(iter, path, path_len))
- continue;
-
- /* convert submodules to GITLINK and remove trailing slashes */
- if (S_ISDIR(statbuf.st_mode)) {
- bool submodule = false;
-
- if ((error = filesystem_iterator_is_submodule(&submodule,
- iter, path, path_len)) < 0)
- goto done;
-
- if (submodule)
- statbuf.st_mode = GIT_FILEMODE_COMMIT;
- }
-
- /* Ensure that the pathlist entry lines up with what we expected */
- else if (dir_expected)
- continue;
-
- entry = filesystem_iterator_entry_init(new_frame,
- path, path_len, &statbuf, pathlist_match);
- GITERR_CHECK_ALLOC(entry);
-
- git_vector_insert(&new_frame->entries, entry);
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- /* sort now that directory suffix is added */
- git_vector_sort(&new_frame->entries);
-
-done:
- if (error < 0)
- git_array_pop(iter->frames);
-
- git_buf_free(&root);
- git_path_diriter_free(&diriter);
- return error;
-}
-
-GIT_INLINE(void) filesystem_iterator_frame_pop(filesystem_iterator *iter)
-{
- filesystem_iterator_frame *frame;
-
- assert(iter->frames.size);
-
- frame = git_array_pop(iter->frames);
- filesystem_iterator_frame_pop_ignores(iter);
-
- git_pool_clear(&frame->entry_pool);
- git_vector_free(&frame->entries);
-}
-
-static void filesystem_iterator_set_current(
- filesystem_iterator *iter,
- filesystem_iterator_entry *entry)
-{
- iter->entry.ctime.seconds = entry->st.st_ctime;
- iter->entry.ctime.nanoseconds = entry->st.st_ctime_nsec;
-
- iter->entry.mtime.seconds = entry->st.st_mtime;
- iter->entry.mtime.nanoseconds = entry->st.st_mtime_nsec;
-
- iter->entry.dev = entry->st.st_dev;
- iter->entry.ino = entry->st.st_ino;
- iter->entry.mode = git_futils_canonical_mode(entry->st.st_mode);
- iter->entry.uid = entry->st.st_uid;
- iter->entry.gid = entry->st.st_gid;
- iter->entry.file_size = entry->st.st_size;
-
- iter->entry.path = entry->path;
-
- iter->current_is_ignored = GIT_IGNORE_UNCHECKED;
-}
-
-static int filesystem_iterator_current(
- const git_index_entry **out, git_iterator *i)
-{
- filesystem_iterator *iter = (filesystem_iterator *)i;
-
- if (!iterator__has_been_accessed(i))
- return iter->base.cb->advance(out, i);
-
- if (!iter->frames.size) {
- *out = NULL;
- return GIT_ITEROVER;
- }
-
- *out = &iter->entry;
- return 0;
-}
-
-static int filesystem_iterator_advance(
- const git_index_entry **out, git_iterator *i)
-{
- filesystem_iterator *iter = (filesystem_iterator *)i;
- int error = 0;
-
- iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
-
- /* examine filesystem entries until we find the next one to return */
- while (true) {
- filesystem_iterator_frame *frame;
- filesystem_iterator_entry *entry;
-
- if ((frame = filesystem_iterator_current_frame(iter)) == NULL) {
- error = GIT_ITEROVER;
- break;
- }
-
- /* no more entries in this frame. pop the frame out */
- if (frame->next_idx == frame->entries.length) {
- filesystem_iterator_frame_pop(iter);
- continue;
- }
-
- /* we have more entries in the current frame, that's our next entry */
- entry = frame->entries.contents[frame->next_idx];
- frame->next_idx++;
-
- if (S_ISDIR(entry->st.st_mode)) {
- if (iterator__do_autoexpand(iter)) {
- error = filesystem_iterator_frame_push(iter, entry);
-
- /* may get GIT_ENOTFOUND due to races or permission problems
- * that we want to quietly swallow
- */
- if (error == GIT_ENOTFOUND)
- continue;
- else if (error < 0)
- break;
- }
-
- if (!iterator__include_trees(iter))
- continue;
- }
-
- filesystem_iterator_set_current(iter, entry);
- break;
- }
-
- if (out)
- *out = (error == 0) ? &iter->entry : NULL;
-
- return error;
-}
-
-static int filesystem_iterator_advance_into(
- const git_index_entry **out, git_iterator *i)
-{
- filesystem_iterator *iter = (filesystem_iterator *)i;
- filesystem_iterator_frame *frame;
- filesystem_iterator_entry *prev_entry;
- int error;
-
- if (out)
- *out = NULL;
-
- if ((frame = filesystem_iterator_current_frame(iter)) == NULL)
- return GIT_ITEROVER;
-
- /* get the last seen entry */
- prev_entry = filesystem_iterator_current_entry(frame);
-
- /* it's legal to call advance_into when auto-expand is on. in this case,
- * we will have pushed a new (empty) frame on to the stack for this
- * new directory. since it's empty, its current_entry should be null.
- */
- assert(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
-
- if (prev_entry) {
- if (prev_entry->st.st_mode != GIT_FILEMODE_COMMIT &&
- !S_ISDIR(prev_entry->st.st_mode))
- return 0;
-
- if ((error = filesystem_iterator_frame_push(iter, prev_entry)) < 0)
- return error;
- }
-
- /* we've advanced into the directory in question, let advance
- * find the first entry
- */
- return filesystem_iterator_advance(out, i);
-}
-
-int git_iterator_current_workdir_path(git_buf **out, git_iterator *i)
-{
- filesystem_iterator *iter = (filesystem_iterator *)i;
- const git_index_entry *entry;
-
- if (i->type != GIT_ITERATOR_TYPE_FS &&
- i->type != GIT_ITERATOR_TYPE_WORKDIR) {
- *out = NULL;
- return 0;
- }
-
- git_buf_truncate(&iter->current_path, iter->root_len);
-
- if (git_iterator_current(&entry, i) < 0 ||
- git_buf_puts(&iter->current_path, entry->path) < 0)
- return -1;
-
- *out = &iter->current_path;
- return 0;
-}
-
-GIT_INLINE(git_dir_flag) entry_dir_flag(git_index_entry *entry)
-{
-#if defined(GIT_WIN32) && !defined(__MINGW32__)
- return (entry && entry->mode) ?
- (S_ISDIR(entry->mode) ? GIT_DIR_FLAG_TRUE : GIT_DIR_FLAG_FALSE) :
- GIT_DIR_FLAG_UNKNOWN;
-#else
- GIT_UNUSED(entry);
- return GIT_DIR_FLAG_UNKNOWN;
-#endif
-}
-
-static void filesystem_iterator_update_ignored(filesystem_iterator *iter)
-{
- filesystem_iterator_frame *frame;
- git_dir_flag dir_flag = entry_dir_flag(&iter->entry);
-
- if (git_ignore__lookup(&iter->current_is_ignored,
- &iter->ignores, iter->entry.path, dir_flag) < 0) {
- giterr_clear();
- iter->current_is_ignored = GIT_IGNORE_NOTFOUND;
- }
-
- /* use ignore from containing frame stack */
- if (iter->current_is_ignored <= GIT_IGNORE_NOTFOUND) {
- frame = filesystem_iterator_current_frame(iter);
- iter->current_is_ignored = frame->is_ignored;
- }
-}
-
-GIT_INLINE(bool) filesystem_iterator_current_is_ignored(
- filesystem_iterator *iter)
-{
- if (iter->current_is_ignored == GIT_IGNORE_UNCHECKED)
- filesystem_iterator_update_ignored(iter);
-
- return (iter->current_is_ignored == GIT_IGNORE_TRUE);
-}
-
-bool git_iterator_current_is_ignored(git_iterator *i)
-{
- if (i->type != GIT_ITERATOR_TYPE_WORKDIR)
- return false;
-
- return filesystem_iterator_current_is_ignored((filesystem_iterator *)i);
-}
-
-bool git_iterator_current_tree_is_ignored(git_iterator *i)
-{
- filesystem_iterator *iter = (filesystem_iterator *)i;
- filesystem_iterator_frame *frame;
-
- if (i->type != GIT_ITERATOR_TYPE_WORKDIR)
- return false;
-
- frame = filesystem_iterator_current_frame(iter);
- return (frame->is_ignored == GIT_IGNORE_TRUE);
-}
-
-static int filesystem_iterator_advance_over(
- const git_index_entry **out,
- git_iterator_status_t *status,
- git_iterator *i)
-{
- filesystem_iterator *iter = (filesystem_iterator *)i;
- filesystem_iterator_frame *current_frame;
- filesystem_iterator_entry *current_entry;
- const git_index_entry *entry = NULL;
- const char *base;
- int error = 0;
-
- *out = NULL;
- *status = GIT_ITERATOR_STATUS_NORMAL;
-
- assert(iterator__has_been_accessed(i));
-
- current_frame = filesystem_iterator_current_frame(iter);
- assert(current_frame);
- current_entry = filesystem_iterator_current_entry(current_frame);
- assert(current_entry);
-
- if ((error = git_iterator_current(&entry, i)) < 0)
- return error;
-
- if (!S_ISDIR(entry->mode)) {
- if (filesystem_iterator_current_is_ignored(iter))
- *status = GIT_ITERATOR_STATUS_IGNORED;
-
- return filesystem_iterator_advance(out, i);
- }
-
- git_buf_clear(&iter->tmp_buf);
- if ((error = git_buf_puts(&iter->tmp_buf, entry->path)) < 0)
- return error;
-
- base = iter->tmp_buf.ptr;
-
- /* scan inside the directory looking for files. if we find nothing,
- * we will remain EMPTY. if we find any ignored item, upgrade EMPTY to
- * IGNORED. if we find a real actual item, upgrade all the way to NORMAL
- * and then stop.
- *
- * however, if we're here looking for a pathlist item (but are not
- * actually in the pathlist ourselves) then start at FILTERED instead of
- * EMPTY. callers then know that this path was not something they asked
- * about.
- */
- *status = current_entry->match == ITERATOR_PATHLIST_IS_PARENT ?
- GIT_ITERATOR_STATUS_FILTERED : GIT_ITERATOR_STATUS_EMPTY;
-
- while (entry && !iter->base.prefixcomp(entry->path, base)) {
- if (filesystem_iterator_current_is_ignored(iter)) {
- /* if we found an explicitly ignored item, then update from
- * EMPTY to IGNORED
- */
- *status = GIT_ITERATOR_STATUS_IGNORED;
- } else if (S_ISDIR(entry->mode)) {
- error = filesystem_iterator_advance_into(&entry, i);
-
- if (!error)
- continue;
-
- /* this directory disappeared, ignore it */
- else if (error == GIT_ENOTFOUND)
- error = 0;
-
- /* a real error occurred */
- else
- break;
- } else {
- /* we found a non-ignored item, treat parent as untracked */
- *status = GIT_ITERATOR_STATUS_NORMAL;
- break;
- }
-
- if ((error = git_iterator_advance(&entry, i)) < 0)
- break;
- }
-
- /* wrap up scan back to base directory */
- while (entry && !iter->base.prefixcomp(entry->path, base)) {
- if ((error = git_iterator_advance(&entry, i)) < 0)
- break;
- }
-
- if (!error)
- *out = entry;
-
- return error;
-}
-
-static void filesystem_iterator_clear(filesystem_iterator *iter)
-{
- while (iter->frames.size)
- filesystem_iterator_frame_pop(iter);
-
- git_array_clear(iter->frames);
- git_ignore__free(&iter->ignores);
-
- git_buf_free(&iter->tmp_buf);
-
- iterator_clear(&iter->base);
-}
-
-static int filesystem_iterator_init(filesystem_iterator *iter)
-{
- int error;
-
- if (iterator__honor_ignores(&iter->base) &&
- (error = git_ignore__for_path(iter->base.repo,
- ".gitignore", &iter->ignores)) < 0)
- return error;
-
- if ((error = filesystem_iterator_frame_push(iter, NULL)) < 0)
- return error;
-
- iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
-
- return 0;
-}
-
-static int filesystem_iterator_reset(git_iterator *i)
-{
- filesystem_iterator *iter = (filesystem_iterator *)i;
-
- filesystem_iterator_clear(iter);
- return filesystem_iterator_init(iter);
-}
-
-static void filesystem_iterator_free(git_iterator *i)
-{
- filesystem_iterator *iter = (filesystem_iterator *)i;
- git__free(iter->root);
- git_buf_free(&iter->current_path);
- git_tree_free(iter->tree);
- if (iter->index)
- git_index_snapshot_release(&iter->index_snapshot, iter->index);
- filesystem_iterator_clear(iter);
-}
-
-static int iterator_for_filesystem(
- git_iterator **out,
- git_repository *repo,
- const char *root,
- git_index *index,
- git_tree *tree,
- git_iterator_type_t type,
- git_iterator_options *options)
-{
- filesystem_iterator *iter;
- size_t root_len;
- int error;
-
- static git_iterator_callbacks callbacks = {
- filesystem_iterator_current,
- filesystem_iterator_advance,
- filesystem_iterator_advance_into,
- filesystem_iterator_advance_over,
- filesystem_iterator_reset,
- filesystem_iterator_free
- };
-
- *out = NULL;
-
- if (root == NULL)
- return git_iterator_for_nothing(out, options);
-
- iter = git__calloc(1, sizeof(filesystem_iterator));
- GITERR_CHECK_ALLOC(iter);
-
- iter->base.type = type;
- iter->base.cb = &callbacks;
-
- root_len = strlen(root);
-
- iter->root = git__malloc(root_len+2);
- GITERR_CHECK_ALLOC(iter->root);
-
- memcpy(iter->root, root, root_len);
-
- if (root_len == 0 || root[root_len-1] != '/') {
- iter->root[root_len] = '/';
- root_len++;
- }
- iter->root[root_len] = '\0';
- iter->root_len = root_len;
-
- if ((error = git_buf_puts(&iter->current_path, iter->root)) < 0)
- goto on_error;
-
- if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0)
- goto on_error;
-
- if (tree && (error = git_tree_dup(&iter->tree, tree)) < 0)
- goto on_error;
-
- if (index &&
- (error = git_index_snapshot_new(&iter->index_snapshot, index)) < 0)
- goto on_error;
-
- iter->index = index;
- iter->dirload_flags =
- (iterator__ignore_case(&iter->base) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
- (iterator__flag(&iter->base, PRECOMPOSE_UNICODE) ?
- GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
-
- if ((error = filesystem_iterator_init(iter)) < 0)
- goto on_error;
-
- *out = &iter->base;
- return 0;
-
-on_error:
- git_iterator_free(&iter->base);
- return error;
-}
-
-int git_iterator_for_filesystem(
- git_iterator **out,
- const char *root,
- git_iterator_options *options)
-{
- return iterator_for_filesystem(out,
- NULL, root, NULL, NULL, GIT_ITERATOR_TYPE_FS, options);
-}
-
-int git_iterator_for_workdir_ext(
- git_iterator **out,
- git_repository *repo,
- const char *repo_workdir,
- git_index *index,
- git_tree *tree,
- git_iterator_options *given_opts)
-{
- git_iterator_options options = GIT_ITERATOR_OPTIONS_INIT;
-
- if (!repo_workdir) {
- if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
- return GIT_EBAREREPO;
-
- repo_workdir = git_repository_workdir(repo);
- }
-
- /* upgrade to a workdir iterator, adding necessary internal flags */
- if (given_opts)
- memcpy(&options, given_opts, sizeof(git_iterator_options));
-
- options.flags |= GIT_ITERATOR_HONOR_IGNORES |
- GIT_ITERATOR_IGNORE_DOT_GIT;
-
- return iterator_for_filesystem(out,
- repo, repo_workdir, index, tree, GIT_ITERATOR_TYPE_WORKDIR, &options);
-}
-
-
-/* Index iterator */
-
-
-typedef struct {
- git_iterator base;
- git_vector entries;
- size_t next_idx;
-
- /* the pseudotree entry */
- git_index_entry tree_entry;
- git_buf tree_buf;
- bool skip_tree;
-
- const git_index_entry *entry;
-} index_iterator;
-
-static int index_iterator_current(
- const git_index_entry **out, git_iterator *i)
-{
- index_iterator *iter = (index_iterator *)i;
-
- if (!iterator__has_been_accessed(i))
- return iter->base.cb->advance(out, i);
-
- if (iter->entry == NULL) {
- *out = NULL;
- return GIT_ITEROVER;
- }
-
- *out = iter->entry;
- return 0;
-}
-
-static bool index_iterator_create_pseudotree(
- const git_index_entry **out,
- index_iterator *iter,
- const char *path)
-{
- const char *prev_path, *relative_path, *dirsep;
- size_t common_len;
-
- prev_path = iter->entry ? iter->entry->path : "";
-
- /* determine if the new path is in a different directory from the old */
- common_len = git_path_common_dirlen(prev_path, path);
- relative_path = path + common_len;
-
- if ((dirsep = strchr(relative_path, '/')) == NULL)
- return false;
-
- git_buf_clear(&iter->tree_buf);
- git_buf_put(&iter->tree_buf, path, (dirsep - path) + 1);
-
- iter->tree_entry.mode = GIT_FILEMODE_TREE;
- iter->tree_entry.path = iter->tree_buf.ptr;
-
- *out = &iter->tree_entry;
- return true;
-}
-
-static int index_iterator_skip_pseudotree(index_iterator *iter)
-{
- assert(iterator__has_been_accessed(&iter->base));
- assert(S_ISDIR(iter->entry->mode));
-
- while (true) {
- const git_index_entry *next_entry = NULL;
-
- if (++iter->next_idx >= iter->entries.length)
- return GIT_ITEROVER;
-
- next_entry = iter->entries.contents[iter->next_idx];
-
- if (iter->base.strncomp(iter->tree_buf.ptr, next_entry->path,
- iter->tree_buf.size) != 0)
- break;
- }
-
- iter->skip_tree = false;
- return 0;
-}
-
-static int index_iterator_advance(
- const git_index_entry **out, git_iterator *i)
-{
- index_iterator *iter = (index_iterator *)i;
- const git_index_entry *entry = NULL;
- bool is_submodule;
- int error = 0;
-
- iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
-
- while (true) {
- if (iter->next_idx >= iter->entries.length) {
- error = GIT_ITEROVER;
- break;
- }
-
- /* we were not asked to expand this pseudotree. advance over it. */
- if (iter->skip_tree) {
- index_iterator_skip_pseudotree(iter);
- continue;
- }
-
- entry = iter->entries.contents[iter->next_idx];
- is_submodule = S_ISGITLINK(entry->mode);
-
- if (!iterator_has_started(&iter->base, entry->path, is_submodule)) {
- iter->next_idx++;
- continue;
- }
-
- if (iterator_has_ended(&iter->base, entry->path)) {
- error = GIT_ITEROVER;
- break;
- }
-
- /* if we have a list of paths we're interested in, examine it */
- if (!iterator_pathlist_next_is(&iter->base, entry->path)) {
- iter->next_idx++;
- continue;
- }
-
- /* if this is a conflict, skip it unless we're including conflicts */
- if (git_index_entry_is_conflict(entry) &&
- !iterator__include_conflicts(&iter->base)) {
- iter->next_idx++;
- continue;
- }
-
- /* we've found what will be our next _file_ entry. but if we are
- * returning trees entries, we may need to return a pseudotree
- * entry that will contain this. don't advance over this entry,
- * though, we still need to return it on the next `advance`.
- */
- if (iterator__include_trees(&iter->base) &&
- index_iterator_create_pseudotree(&entry, iter, entry->path)) {
-
- /* Note whether this pseudo tree should be expanded or not */
- iter->skip_tree = iterator__dont_autoexpand(&iter->base);
- break;
- }
-
- iter->next_idx++;
- break;
- }
-
- iter->entry = (error == 0) ? entry : NULL;
-
- if (out)
- *out = iter->entry;
-
- return error;
-}
-
-static int index_iterator_advance_into(
- const git_index_entry **out, git_iterator *i)
-{
- index_iterator *iter = (index_iterator *)i;
-
- if (! S_ISDIR(iter->tree_entry.mode)) {
- if (out)
- *out = NULL;
-
- return 0;
- }
-
- iter->skip_tree = false;
- return index_iterator_advance(out, i);
-}
-
-static int index_iterator_advance_over(
- const git_index_entry **out,
- git_iterator_status_t *status,
- git_iterator *i)
-{
- index_iterator *iter = (index_iterator *)i;
- const git_index_entry *entry;
- int error;
-
- if ((error = index_iterator_current(&entry, i)) < 0)
- return error;
-
- if (S_ISDIR(entry->mode))
- index_iterator_skip_pseudotree(iter);
-
- *status = GIT_ITERATOR_STATUS_NORMAL;
- return index_iterator_advance(out, i);
-}
-
-static void index_iterator_clear(index_iterator *iter)
-{
- iterator_clear(&iter->base);
-}
-
-static int index_iterator_init(index_iterator *iter)
-{
- iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
- iter->next_idx = 0;
- iter->skip_tree = false;
- return 0;
-}
-
-static int index_iterator_reset(git_iterator *i)
-{
- index_iterator *iter = (index_iterator *)i;
-
- index_iterator_clear(iter);
- return index_iterator_init(iter);
-}
-
-static void index_iterator_free(git_iterator *i)
-{
- index_iterator *iter = (index_iterator *)i;
-
- git_index_snapshot_release(&iter->entries, iter->base.index);
- git_buf_free(&iter->tree_buf);
-}
-
-int git_iterator_for_index(
- git_iterator **out,
- git_repository *repo,
- git_index *index,
- git_iterator_options *options)
-{
- index_iterator *iter;
- int error;
-
- static git_iterator_callbacks callbacks = {
- index_iterator_current,
- index_iterator_advance,
- index_iterator_advance_into,
- index_iterator_advance_over,
- index_iterator_reset,
- index_iterator_free
- };
-
- *out = NULL;
-
- if (index == NULL)
- return git_iterator_for_nothing(out, options);
-
- iter = git__calloc(1, sizeof(index_iterator));
- GITERR_CHECK_ALLOC(iter);
-
- iter->base.type = GIT_ITERATOR_TYPE_INDEX;
- iter->base.cb = &callbacks;
-
- if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0 ||
- (error = git_index_snapshot_new(&iter->entries, index)) < 0 ||
- (error = index_iterator_init(iter)) < 0)
- goto on_error;
-
- git_vector_set_cmp(&iter->entries, iterator__ignore_case(&iter->base) ?
- git_index_entry_icmp : git_index_entry_cmp);
- git_vector_sort(&iter->entries);
-
- *out = &iter->base;
- return 0;
-
-on_error:
- git_iterator_free(&iter->base);
- return error;
-}
-
-
-/* Iterator API */
-
-int git_iterator_reset_range(
- git_iterator *i, const char *start, const char *end)
-{
- if (iterator_reset_range(i, start, end) < 0)
- return -1;
-
- return i->cb->reset(i);
-}
-
-void git_iterator_set_ignore_case(git_iterator *i, bool ignore_case)
-{
- assert(!iterator__has_been_accessed(i));
- iterator_set_ignore_case(i, ignore_case);
-}
-
-void git_iterator_free(git_iterator *iter)
-{
- if (iter == NULL)
- return;
-
- iter->cb->free(iter);
-
- git_vector_free(&iter->pathlist);
- git__free(iter->start);
- git__free(iter->end);
-
- memset(iter, 0, sizeof(*iter));
-
- git__free(iter);
-}
-
-int git_iterator_walk(
- git_iterator **iterators,
- size_t cnt,
- git_iterator_walk_cb cb,
- void *data)
-{
- const git_index_entry **iterator_item; /* next in each iterator */
- const git_index_entry **cur_items; /* current path in each iter */
- const git_index_entry *first_match;
- size_t i, j;
- int error = 0;
-
- iterator_item = git__calloc(cnt, sizeof(git_index_entry *));
- cur_items = git__calloc(cnt, sizeof(git_index_entry *));
-
- GITERR_CHECK_ALLOC(iterator_item);
- GITERR_CHECK_ALLOC(cur_items);
-
- /* Set up the iterators */
- for (i = 0; i < cnt; i++) {
- error = git_iterator_current(&iterator_item[i], iterators[i]);
-
- if (error < 0 && error != GIT_ITEROVER)
- goto done;
- }
-
- while (true) {
- for (i = 0; i < cnt; i++)
- cur_items[i] = NULL;
-
- first_match = NULL;
-
- /* Find the next path(s) to consume from each iterator */
- for (i = 0; i < cnt; i++) {
- if (iterator_item[i] == NULL)
- continue;
-
- if (first_match == NULL) {
- first_match = iterator_item[i];
- cur_items[i] = iterator_item[i];
- } else {
- int path_diff = git_index_entry_cmp(iterator_item[i], first_match);
-
- if (path_diff < 0) {
- /* Found an index entry that sorts before the one we're
- * looking at. Forget that we've seen the other and
- * look at the other iterators for this path.
- */
- for (j = 0; j < i; j++)
- cur_items[j] = NULL;
-
- first_match = iterator_item[i];
- cur_items[i] = iterator_item[i];
- } else if (path_diff == 0) {
- cur_items[i] = iterator_item[i];
- }
- }
- }
-
- if (first_match == NULL)
- break;
-
- if ((error = cb(cur_items, data)) != 0)
- goto done;
-
- /* Advance each iterator that participated */
- for (i = 0; i < cnt; i++) {
- if (cur_items[i] == NULL)
- continue;
-
- error = git_iterator_advance(&iterator_item[i], iterators[i]);
-
- if (error < 0 && error != GIT_ITEROVER)
- goto done;
- }
- }
-
-done:
- git__free((git_index_entry **)iterator_item);
- git__free((git_index_entry **)cur_items);
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_iterator_h__
-#define INCLUDE_iterator_h__
-
-#include "common.h"
-#include "git2/index.h"
-#include "vector.h"
-#include "buffer.h"
-#include "ignore.h"
-
-typedef struct git_iterator git_iterator;
-
-typedef enum {
- GIT_ITERATOR_TYPE_EMPTY = 0,
- GIT_ITERATOR_TYPE_TREE = 1,
- GIT_ITERATOR_TYPE_INDEX = 2,
- GIT_ITERATOR_TYPE_WORKDIR = 3,
- GIT_ITERATOR_TYPE_FS = 4,
-} git_iterator_type_t;
-
-typedef enum {
- /** ignore case for entry sort order */
- GIT_ITERATOR_IGNORE_CASE = (1u << 0),
- /** force case sensitivity for entry sort order */
- GIT_ITERATOR_DONT_IGNORE_CASE = (1u << 1),
- /** return tree items in addition to blob items */
- GIT_ITERATOR_INCLUDE_TREES = (1u << 2),
- /** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */
- GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3),
- /** convert precomposed unicode to decomposed unicode */
- GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4),
- /** never convert precomposed unicode to decomposed unicode */
- GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE = (1u << 5),
- /** include conflicts */
- GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 6),
-} git_iterator_flag_t;
-
-typedef enum {
- GIT_ITERATOR_STATUS_NORMAL = 0,
- GIT_ITERATOR_STATUS_IGNORED = 1,
- GIT_ITERATOR_STATUS_EMPTY = 2,
- GIT_ITERATOR_STATUS_FILTERED = 3
-} git_iterator_status_t;
-
-typedef struct {
- const char *start;
- const char *end;
-
- /* paths to include in the iterator (literal). if set, any paths not
- * listed here will be excluded from iteration.
- */
- git_strarray pathlist;
-
- /* flags, from above */
- unsigned int flags;
-} git_iterator_options;
-
-#define GIT_ITERATOR_OPTIONS_INIT {0}
-
-typedef struct {
- int (*current)(const git_index_entry **, git_iterator *);
- int (*advance)(const git_index_entry **, git_iterator *);
- int (*advance_into)(const git_index_entry **, git_iterator *);
- int (*advance_over)(
- const git_index_entry **, git_iterator_status_t *, git_iterator *);
- int (*reset)(git_iterator *);
- void (*free)(git_iterator *);
-} git_iterator_callbacks;
-
-struct git_iterator {
- git_iterator_type_t type;
- git_iterator_callbacks *cb;
-
- git_repository *repo;
- git_index *index;
-
- char *start;
- size_t start_len;
-
- char *end;
- size_t end_len;
-
- bool started;
- bool ended;
- git_vector pathlist;
- size_t pathlist_walk_idx;
- int (*strcomp)(const char *a, const char *b);
- int (*strncomp)(const char *a, const char *b, size_t n);
- int (*prefixcomp)(const char *str, const char *prefix);
- int (*entry_srch)(const void *key, const void *array_member);
- size_t stat_calls;
- unsigned int flags;
-};
-
-extern int git_iterator_for_nothing(
- git_iterator **out,
- git_iterator_options *options);
-
-/* tree iterators will match the ignore_case value from the index of the
- * repository, unless you override with a non-zero flag value
- */
-extern int git_iterator_for_tree(
- git_iterator **out,
- git_tree *tree,
- git_iterator_options *options);
-
-/* index iterators will take the ignore_case value from the index; the
- * ignore_case flags are not used
- */
-extern int git_iterator_for_index(
- git_iterator **out,
- git_repository *repo,
- git_index *index,
- git_iterator_options *options);
-
-extern int git_iterator_for_workdir_ext(
- git_iterator **out,
- git_repository *repo,
- const char *repo_workdir,
- git_index *index,
- git_tree *tree,
- git_iterator_options *options);
-
-/* workdir iterators will match the ignore_case value from the index of the
- * repository, unless you override with a non-zero flag value
- */
-GIT_INLINE(int) git_iterator_for_workdir(
- git_iterator **out,
- git_repository *repo,
- git_index *index,
- git_tree *tree,
- git_iterator_options *options)
-{
- return git_iterator_for_workdir_ext(out, repo, NULL, index, tree, options);
-}
-
-/* for filesystem iterators, you have to explicitly pass in the ignore_case
- * behavior that you desire
- */
-extern int git_iterator_for_filesystem(
- git_iterator **out,
- const char *root,
- git_iterator_options *options);
-
-extern void git_iterator_free(git_iterator *iter);
-
-/* Return a git_index_entry structure for the current value the iterator
- * is looking at or NULL if the iterator is at the end.
- *
- * The entry may noy be fully populated. Tree iterators will only have a
- * value mode, OID, and path. Workdir iterators will not have an OID (but
- * you can use `git_iterator_current_oid()` to calculate it on demand).
- *
- * You do not need to free the entry. It is still "owned" by the iterator.
- * Once you call `git_iterator_advance()` then the old entry is no longer
- * guaranteed to be valid - it may be freed or just overwritten in place.
- */
-GIT_INLINE(int) git_iterator_current(
- const git_index_entry **entry, git_iterator *iter)
-{
- return iter->cb->current(entry, iter);
-}
-
-/**
- * Advance to the next item for the iterator.
- *
- * If GIT_ITERATOR_INCLUDE_TREES is set, this may be a tree item. If
- * GIT_ITERATOR_DONT_AUTOEXPAND is set, calling this again when on a tree
- * item will skip over all the items under that tree.
- */
-GIT_INLINE(int) git_iterator_advance(
- const git_index_entry **entry, git_iterator *iter)
-{
- return iter->cb->advance(entry, iter);
-}
-
-/**
- * Iterate into a tree item (when GIT_ITERATOR_DONT_AUTOEXPAND is set).
- *
- * git_iterator_advance() steps through all items being iterated over
- * (either with or without trees, depending on GIT_ITERATOR_INCLUDE_TREES),
- * but if GIT_ITERATOR_DONT_AUTOEXPAND is set, it will skip to the next
- * sibling of a tree instead of going to the first child of the tree. In
- * that case, use this function to advance to the first child of the tree.
- *
- * If the current item is not a tree, this is a no-op.
- *
- * For filesystem and working directory iterators, a tree (i.e. directory)
- * can be empty. In that case, this function returns GIT_ENOTFOUND and
- * does not advance. That can't happen for tree and index iterators.
- */
-GIT_INLINE(int) git_iterator_advance_into(
- const git_index_entry **entry, git_iterator *iter)
-{
- return iter->cb->advance_into(entry, iter);
-}
-
-/* Advance over a directory and check if it contains no files or just
- * ignored files.
- *
- * In a tree or the index, all directories will contain files, but in the
- * working directory it is possible to have an empty directory tree or a
- * tree that only contains ignored files. Many Git operations treat these
- * cases specially. This advances over a directory (presumably an
- * untracked directory) but checks during the scan if there are any files
- * and any non-ignored files.
- */
-GIT_INLINE(int) git_iterator_advance_over(
- const git_index_entry **entry,
- git_iterator_status_t *status,
- git_iterator *iter)
-{
- return iter->cb->advance_over(entry, status, iter);
-}
-
-/**
- * Go back to the start of the iteration.
- */
-GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
-{
- return iter->cb->reset(iter);
-}
-
-/**
- * Go back to the start of the iteration after updating the `start` and
- * `end` pathname boundaries of the iteration.
- */
-extern int git_iterator_reset_range(
- git_iterator *iter, const char *start, const char *end);
-
-GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter)
-{
- return iter->type;
-}
-
-GIT_INLINE(git_repository *) git_iterator_owner(git_iterator *iter)
-{
- return iter->repo;
-}
-
-GIT_INLINE(git_index *) git_iterator_index(git_iterator *iter)
-{
- return iter->index;
-}
-
-GIT_INLINE(git_iterator_flag_t) git_iterator_flags(git_iterator *iter)
-{
- return iter->flags;
-}
-
-GIT_INLINE(bool) git_iterator_ignore_case(git_iterator *iter)
-{
- return ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0);
-}
-
-extern void git_iterator_set_ignore_case(
- git_iterator *iter, bool ignore_case);
-
-extern int git_iterator_current_tree_entry(
- const git_tree_entry **entry_out, git_iterator *iter);
-
-extern int git_iterator_current_parent_tree(
- const git_tree **tree_out, git_iterator *iter, size_t depth);
-
-extern bool git_iterator_current_is_ignored(git_iterator *iter);
-
-extern bool git_iterator_current_tree_is_ignored(git_iterator *iter);
-
-/**
- * Get full path of the current item from a workdir iterator. This will
- * return NULL for a non-workdir iterator. The git_buf is still owned by
- * the iterator; this is exposed just for efficiency.
- */
-extern int git_iterator_current_workdir_path(
- git_buf **path, git_iterator *iter);
-
-/**
- * Retrieve the index stored in the iterator.
- *
- * Only implemented for the workdir and index iterators.
- */
-extern git_index *git_iterator_index(git_iterator *iter);
-
-typedef int (*git_iterator_walk_cb)(
- const git_index_entry **entries,
- void *data);
-
-/**
- * Walk the given iterators in lock-step. The given callback will be
- * called for each unique path, with the index entry in each iterator
- * (or NULL if the given iterator does not contain that path).
- */
-extern int git_iterator_walk(
- git_iterator **iterators,
- size_t cnt,
- git_iterator_walk_cb cb,
- void *data);
-
-#endif
+++ /dev/null
-/* The MIT License
-
- Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-/*
- An example:
-
-#include "khash.h"
-KHASH_MAP_INIT_INT(32, char)
-int main() {
- int ret, is_missing;
- khiter_t k;
- khash_t(32) *h = kh_init(32);
- k = kh_put(32, h, 5, &ret);
- kh_value(h, k) = 10;
- k = kh_get(32, h, 10);
- is_missing = (k == kh_end(h));
- k = kh_get(32, h, 5);
- kh_del(32, h, k);
- for (k = kh_begin(h); k != kh_end(h); ++k)
- if (kh_exist(h, k)) kh_value(h, k) = 1;
- kh_destroy(32, h);
- return 0;
-}
-*/
-
-/*
- 2013-05-02 (0.2.8):
-
- * Use quadratic probing. When the capacity is power of 2, stepping function
- i*(i+1)/2 guarantees to traverse each bucket. It is better than double
- hashing on cache performance and is more robust than linear probing.
-
- In theory, double hashing should be more robust than quadratic probing.
- However, my implementation is probably not for large hash tables, because
- the second hash function is closely tied to the first hash function,
- which reduce the effectiveness of double hashing.
-
- Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
-
- 2011-12-29 (0.2.7):
-
- * Minor code clean up; no actual effect.
-
- 2011-09-16 (0.2.6):
-
- * The capacity is a power of 2. This seems to dramatically improve the
- speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
-
- - http://code.google.com/p/ulib/
- - http://nothings.org/computer/judy/
-
- * Allow to optionally use linear probing which usually has better
- performance for random input. Double hashing is still the default as it
- is more robust to certain non-random input.
-
- * Added Wang's integer hash function (not used by default). This hash
- function is more robust to certain non-random input.
-
- 2011-02-14 (0.2.5):
-
- * Allow to declare global functions.
-
- 2009-09-26 (0.2.4):
-
- * Improve portability
-
- 2008-09-19 (0.2.3):
-
- * Corrected the example
- * Improved interfaces
-
- 2008-09-11 (0.2.2):
-
- * Improved speed a little in kh_put()
-
- 2008-09-10 (0.2.1):
-
- * Added kh_clear()
- * Fixed a compiling error
-
- 2008-09-02 (0.2.0):
-
- * Changed to token concatenation which increases flexibility.
-
- 2008-08-31 (0.1.2):
-
- * Fixed a bug in kh_get(), which has not been tested previously.
-
- 2008-08-31 (0.1.1):
-
- * Added destructor
-*/
-
-
-#ifndef __AC_KHASH_H
-#define __AC_KHASH_H
-
-/*!
- @header
-
- Generic hash table library.
- */
-
-#define AC_VERSION_KHASH_H "0.2.8"
-
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-/* compiler specific configuration */
-
-#if UINT_MAX == 0xffffffffu
-typedef unsigned int khint32_t;
-#elif ULONG_MAX == 0xffffffffu
-typedef unsigned long khint32_t;
-#endif
-
-#if ULONG_MAX == ULLONG_MAX
-typedef unsigned long khint64_t;
-#else
-typedef unsigned long long khint64_t;
-#endif
-
-#ifndef kh_inline
-#ifdef _MSC_VER
-#define kh_inline __inline
-#else
-#define kh_inline inline
-#endif
-#endif /* kh_inline */
-
-typedef khint32_t khint_t;
-typedef khint_t khiter_t;
-
-#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
-#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
-#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
-#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
-#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
-#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
-#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
-
-#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
-
-#ifndef kroundup32
-#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
-#endif
-
-#ifndef kcalloc
-#define kcalloc(N,Z) calloc(N,Z)
-#endif
-#ifndef kmalloc
-#define kmalloc(Z) malloc(Z)
-#endif
-#ifndef krealloc
-#define krealloc(P,Z) realloc(P,Z)
-#endif
-#ifndef kreallocarray
-#define kreallocarray(P,N,Z) ((SIZE_MAX - N < Z) ? NULL : krealloc(P, (N*Z)))
-#endif
-#ifndef kfree
-#define kfree(P) free(P)
-#endif
-
-static const double __ac_HASH_UPPER = 0.77;
-
-#define __KHASH_TYPE(name, khkey_t, khval_t) \
- typedef struct kh_##name##_s { \
- khint_t n_buckets, size, n_occupied, upper_bound; \
- khint32_t *flags; \
- khkey_t *keys; \
- khval_t *vals; \
- } kh_##name##_t;
-
-#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
- extern kh_##name##_t *kh_init_##name(void); \
- extern void kh_destroy_##name(kh_##name##_t *h); \
- extern void kh_clear_##name(kh_##name##_t *h); \
- extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
- extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
- extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
- extern void kh_del_##name(kh_##name##_t *h, khint_t x);
-
-#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
- SCOPE kh_##name##_t *kh_init_##name(void) { \
- return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
- } \
- SCOPE void kh_destroy_##name(kh_##name##_t *h) \
- { \
- if (h) { \
- kfree((void *)h->keys); kfree(h->flags); \
- kfree((void *)h->vals); \
- kfree(h); \
- } \
- } \
- SCOPE void kh_clear_##name(kh_##name##_t *h) \
- { \
- if (h && h->flags) { \
- memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
- h->size = h->n_occupied = 0; \
- } \
- } \
- SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
- { \
- if (h->n_buckets) { \
- khint_t k, i, last, mask, step = 0; \
- mask = h->n_buckets - 1; \
- k = __hash_func(key); i = k & mask; \
- last = i; \
- while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
- i = (i + (++step)) & mask; \
- if (i == last) return h->n_buckets; \
- } \
- return __ac_iseither(h->flags, i)? h->n_buckets : i; \
- } else return 0; \
- } \
- SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
- { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
- khint32_t *new_flags = 0; \
- khint_t j = 1; \
- { \
- kroundup32(new_n_buckets); \
- if (new_n_buckets < 4) new_n_buckets = 4; \
- if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
- else { /* hash table size to be changed (shrink or expand); rehash */ \
- new_flags = (khint32_t*)kreallocarray(NULL, __ac_fsize(new_n_buckets), sizeof(khint32_t)); \
- if (!new_flags) return -1; \
- memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
- if (h->n_buckets < new_n_buckets) { /* expand */ \
- khkey_t *new_keys = (khkey_t*)kreallocarray((void *)h->keys, new_n_buckets, sizeof(khkey_t)); \
- if (!new_keys) { kfree(new_flags); return -1; } \
- h->keys = new_keys; \
- if (kh_is_map) { \
- khval_t *new_vals = (khval_t*)kreallocarray((void *)h->vals, new_n_buckets, sizeof(khval_t)); \
- if (!new_vals) { kfree(new_flags); return -1; } \
- h->vals = new_vals; \
- } \
- } /* otherwise shrink */ \
- } \
- } \
- if (j) { /* rehashing is needed */ \
- for (j = 0; j != h->n_buckets; ++j) { \
- if (__ac_iseither(h->flags, j) == 0) { \
- khkey_t key = h->keys[j]; \
- khval_t val; \
- khint_t new_mask; \
- new_mask = new_n_buckets - 1; \
- if (kh_is_map) val = h->vals[j]; \
- __ac_set_isdel_true(h->flags, j); \
- while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
- khint_t k, i, step = 0; \
- k = __hash_func(key); \
- i = k & new_mask; \
- while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
- __ac_set_isempty_false(new_flags, i); \
- if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
- { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
- if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
- __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
- } else { /* write the element and jump out of the loop */ \
- h->keys[i] = key; \
- if (kh_is_map) h->vals[i] = val; \
- break; \
- } \
- } \
- } \
- } \
- if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
- h->keys = (khkey_t*)kreallocarray((void *)h->keys, new_n_buckets, sizeof(khkey_t)); \
- if (kh_is_map) h->vals = (khval_t*)kreallocarray((void *)h->vals, new_n_buckets, sizeof(khval_t)); \
- } \
- kfree(h->flags); /* free the working space */ \
- h->flags = new_flags; \
- h->n_buckets = new_n_buckets; \
- h->n_occupied = h->size; \
- h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
- } \
- return 0; \
- } \
- SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
- { \
- khint_t x; \
- if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
- if (h->n_buckets > (h->size<<1)) { \
- if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
- *ret = -1; return h->n_buckets; \
- } \
- } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
- *ret = -1; return h->n_buckets; \
- } \
- } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
- { \
- khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
- x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
- if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
- else { \
- last = i; \
- while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
- if (__ac_isdel(h->flags, i)) site = i; \
- i = (i + (++step)) & mask; \
- if (i == last) { x = site; break; } \
- } \
- if (x == h->n_buckets) { \
- if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
- else x = i; \
- } \
- } \
- } \
- if (__ac_isempty(h->flags, x)) { /* not present at all */ \
- h->keys[x] = key; \
- __ac_set_isboth_false(h->flags, x); \
- ++h->size; ++h->n_occupied; \
- *ret = 1; \
- } else if (__ac_isdel(h->flags, x)) { /* deleted */ \
- h->keys[x] = key; \
- __ac_set_isboth_false(h->flags, x); \
- ++h->size; \
- *ret = 2; \
- } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
- return x; \
- } \
- SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
- { \
- if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
- __ac_set_isdel_true(h->flags, x); \
- --h->size; \
- } \
- }
-
-#define KHASH_DECLARE(name, khkey_t, khval_t) \
- __KHASH_TYPE(name, khkey_t, khval_t) \
- __KHASH_PROTOTYPES(name, khkey_t, khval_t)
-
-#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
- __KHASH_TYPE(name, khkey_t, khval_t) \
- __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
-
-#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
- KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
-
-/* --- BEGIN OF HASH FUNCTIONS --- */
-
-/*! @function
- @abstract Integer hash function
- @param key The integer [khint32_t]
- @return The hash value [khint_t]
- */
-#define kh_int_hash_func(key) (khint32_t)(key)
-/*! @function
- @abstract Integer comparison function
- */
-#define kh_int_hash_equal(a, b) ((a) == (b))
-/*! @function
- @abstract 64-bit integer hash function
- @param key The integer [khint64_t]
- @return The hash value [khint_t]
- */
-#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
-/*! @function
- @abstract 64-bit integer comparison function
- */
-#define kh_int64_hash_equal(a, b) ((a) == (b))
-/*! @function
- @abstract const char* hash function
- @param s Pointer to a null terminated string
- @return The hash value
- */
-static kh_inline khint_t __ac_X31_hash_string(const char *s)
-{
- khint_t h = (khint_t)*s;
- if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
- return h;
-}
-/*! @function
- @abstract Another interface to const char* hash function
- @param key Pointer to a null terminated string [const char*]
- @return The hash value [khint_t]
- */
-#define kh_str_hash_func(key) __ac_X31_hash_string(key)
-/*! @function
- @abstract Const char* comparison function
- */
-#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
-
-static kh_inline khint_t __ac_Wang_hash(khint_t key)
-{
- key += ~(key << 15);
- key ^= (key >> 10);
- key += (key << 3);
- key ^= (key >> 6);
- key += ~(key << 11);
- key ^= (key >> 16);
- return key;
-}
-#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
-
-/* --- END OF HASH FUNCTIONS --- */
-
-/* Other convenient macros... */
-
-/*!
- @abstract Type of the hash table.
- @param name Name of the hash table [symbol]
- */
-#define khash_t(name) kh_##name##_t
-
-/*! @function
- @abstract Initiate a hash table.
- @param name Name of the hash table [symbol]
- @return Pointer to the hash table [khash_t(name)*]
- */
-#define kh_init(name) kh_init_##name()
-
-/*! @function
- @abstract Destroy a hash table.
- @param name Name of the hash table [symbol]
- @param h Pointer to the hash table [khash_t(name)*]
- */
-#define kh_destroy(name, h) kh_destroy_##name(h)
-
-/*! @function
- @abstract Reset a hash table without deallocating memory.
- @param name Name of the hash table [symbol]
- @param h Pointer to the hash table [khash_t(name)*]
- */
-#define kh_clear(name, h) kh_clear_##name(h)
-
-/*! @function
- @abstract Resize a hash table.
- @param name Name of the hash table [symbol]
- @param h Pointer to the hash table [khash_t(name)*]
- @param s New size [khint_t]
- */
-#define kh_resize(name, h, s) kh_resize_##name(h, s)
-
-/*! @function
- @abstract Insert a key to the hash table.
- @param name Name of the hash table [symbol]
- @param h Pointer to the hash table [khash_t(name)*]
- @param k Key [type of keys]
- @param r Extra return code: -1 if the operation failed;
- 0 if the key is present in the hash table;
- 1 if the bucket is empty (never used); 2 if the element in
- the bucket has been deleted [int*]
- @return Iterator to the inserted element [khint_t]
- */
-#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
-
-/*! @function
- @abstract Retrieve a key from the hash table.
- @param name Name of the hash table [symbol]
- @param h Pointer to the hash table [khash_t(name)*]
- @param k Key [type of keys]
- @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
- */
-#define kh_get(name, h, k) kh_get_##name(h, k)
-
-/*! @function
- @abstract Remove a key from the hash table.
- @param name Name of the hash table [symbol]
- @param h Pointer to the hash table [khash_t(name)*]
- @param k Iterator to the element to be deleted [khint_t]
- */
-#define kh_del(name, h, k) kh_del_##name(h, k)
-
-/*! @function
- @abstract Test whether a bucket contains data.
- @param h Pointer to the hash table [khash_t(name)*]
- @param x Iterator to the bucket [khint_t]
- @return 1 if containing data; 0 otherwise [int]
- */
-#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
-
-/*! @function
- @abstract Get key given an iterator
- @param h Pointer to the hash table [khash_t(name)*]
- @param x Iterator to the bucket [khint_t]
- @return Key [type of keys]
- */
-#define kh_key(h, x) ((h)->keys[x])
-
-/*! @function
- @abstract Get value given an iterator
- @param h Pointer to the hash table [khash_t(name)*]
- @param x Iterator to the bucket [khint_t]
- @return Value [type of values]
- @discussion For hash sets, calling this results in segfault.
- */
-#define kh_val(h, x) ((h)->vals[x])
-
-/*! @function
- @abstract Alias of kh_val()
- */
-#define kh_value(h, x) ((h)->vals[x])
-
-/*! @function
- @abstract Get the start iterator
- @param h Pointer to the hash table [khash_t(name)*]
- @return The start iterator [khint_t]
- */
-#define kh_begin(h) (khint_t)(0)
-
-/*! @function
- @abstract Get the end iterator
- @param h Pointer to the hash table [khash_t(name)*]
- @return The end iterator [khint_t]
- */
-#define kh_end(h) ((h)->n_buckets)
-
-/*! @function
- @abstract Get the number of elements in the hash table
- @param h Pointer to the hash table [khash_t(name)*]
- @return Number of elements in the hash table [khint_t]
- */
-#define kh_size(h) ((h)->size)
-
-/*! @function
- @abstract Get the number of buckets in the hash table
- @param h Pointer to the hash table [khash_t(name)*]
- @return Number of buckets in the hash table [khint_t]
- */
-#define kh_n_buckets(h) ((h)->n_buckets)
-
-/*! @function
- @abstract Iterate over the entries in the hash table
- @param h Pointer to the hash table [khash_t(name)*]
- @param kvar Variable to which key will be assigned
- @param vvar Variable to which value will be assigned
- @param code Block of code to execute
- */
-#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
- for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
- if (!kh_exist(h,__i)) continue; \
- (kvar) = kh_key(h,__i); \
- (vvar) = kh_val(h,__i); \
- code; \
- } }
-
-/*! @function
- @abstract Iterate over the values in the hash table
- @param h Pointer to the hash table [khash_t(name)*]
- @param vvar Variable to which value will be assigned
- @param code Block of code to execute
- */
-#define kh_foreach_value(h, vvar, code) { khint_t __i; \
- for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
- if (!kh_exist(h,__i)) continue; \
- (vvar) = kh_val(h,__i); \
- code; \
- } }
-
-/* More conenient interfaces */
-
-/*! @function
- @abstract Instantiate a hash set containing integer keys
- @param name Name of the hash table [symbol]
- */
-#define KHASH_SET_INIT_INT(name) \
- KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
-
-/*! @function
- @abstract Instantiate a hash map containing integer keys
- @param name Name of the hash table [symbol]
- @param khval_t Type of values [type]
- */
-#define KHASH_MAP_INIT_INT(name, khval_t) \
- KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
-
-/*! @function
- @abstract Instantiate a hash map containing 64-bit integer keys
- @param name Name of the hash table [symbol]
- */
-#define KHASH_SET_INIT_INT64(name) \
- KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
-
-/*! @function
- @abstract Instantiate a hash map containing 64-bit integer keys
- @param name Name of the hash table [symbol]
- @param khval_t Type of values [type]
- */
-#define KHASH_MAP_INIT_INT64(name, khval_t) \
- KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
-
-typedef const char *kh_cstr_t;
-/*! @function
- @abstract Instantiate a hash map containing const char* keys
- @param name Name of the hash table [symbol]
- */
-#define KHASH_SET_INIT_STR(name) \
- KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
-
-/*! @function
- @abstract Instantiate a hash map containing const char* keys
- @param name Name of the hash table [symbol]
- @param khval_t Type of values [type]
- */
-#define KHASH_MAP_INIT_STR(name, khval_t) \
- KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
-
-#endif /* __AC_KHASH_H */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_map_h__
-#define INCLUDE_map_h__
-
-#include "common.h"
-
-
-/* p_mmap() prot values */
-#define GIT_PROT_NONE 0x0
-#define GIT_PROT_READ 0x1
-#define GIT_PROT_WRITE 0x2
-#define GIT_PROT_EXEC 0x4
-
-/* git__mmmap() flags values */
-#define GIT_MAP_FILE 0
-#define GIT_MAP_SHARED 1
-#define GIT_MAP_PRIVATE 2
-#define GIT_MAP_TYPE 0xf
-#define GIT_MAP_FIXED 0x10
-
-#ifdef __amigaos4__
-#define MAP_FAILED 0
-#endif
-
-typedef struct { /* memory mapped buffer */
- void *data; /* data bytes */
- size_t len; /* data length */
-#ifdef GIT_WIN32
- HANDLE fmh; /* file mapping handle */
-#endif
-} git_map;
-
-#define GIT_MMAP_VALIDATE(out, len, prot, flags) do { \
- assert(out != NULL && len > 0); \
- assert((prot & GIT_PROT_WRITE) || (prot & GIT_PROT_READ)); \
- assert((flags & GIT_MAP_FIXED) == 0); } while (0)
-
-extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset);
-extern int p_munmap(git_map *map);
-
-#endif /* INCLUDE_map_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "posix.h"
-#include "buffer.h"
-#include "repository.h"
-#include "revwalk.h"
-#include "commit_list.h"
-#include "merge.h"
-#include "path.h"
-#include "refs.h"
-#include "object.h"
-#include "iterator.h"
-#include "refs.h"
-#include "diff.h"
-#include "diff_generate.h"
-#include "diff_tform.h"
-#include "checkout.h"
-#include "tree.h"
-#include "blob.h"
-#include "oid.h"
-#include "index.h"
-#include "filebuf.h"
-#include "config.h"
-#include "oidarray.h"
-#include "annotated_commit.h"
-#include "commit.h"
-#include "oidarray.h"
-#include "merge_driver.h"
-
-#include "git2/types.h"
-#include "git2/repository.h"
-#include "git2/object.h"
-#include "git2/commit.h"
-#include "git2/merge.h"
-#include "git2/refs.h"
-#include "git2/reset.h"
-#include "git2/checkout.h"
-#include "git2/signature.h"
-#include "git2/config.h"
-#include "git2/tree.h"
-#include "git2/oidarray.h"
-#include "git2/annotated_commit.h"
-#include "git2/sys/index.h"
-#include "git2/sys/hashsig.h"
-
-#define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0)
-#define GIT_MERGE_INDEX_ENTRY_ISFILE(X) S_ISREG((X).mode)
-
-
-typedef enum {
- TREE_IDX_ANCESTOR = 0,
- TREE_IDX_OURS = 1,
- TREE_IDX_THEIRS = 2
-} merge_tree_index_t;
-
-/* Tracks D/F conflicts */
-struct merge_diff_df_data {
- const char *df_path;
- const char *prev_path;
- git_merge_diff *prev_conflict;
-};
-
-/* Merge base computation */
-
-int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[])
-{
- git_revwalk *walk = NULL;
- git_vector list;
- git_commit_list *result = NULL;
- git_commit_list_node *commit;
- int error = -1;
- unsigned int i;
-
- if (length < 2) {
- giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length);
- return -1;
- }
-
- if (git_vector_init(&list, length - 1, NULL) < 0)
- return -1;
-
- if (git_revwalk_new(&walk, repo) < 0)
- goto on_error;
-
- for (i = 1; i < length; i++) {
- commit = git_revwalk__commit_lookup(walk, &input_array[i]);
- if (commit == NULL)
- goto on_error;
-
- git_vector_insert(&list, commit);
- }
-
- commit = git_revwalk__commit_lookup(walk, &input_array[0]);
- if (commit == NULL)
- goto on_error;
-
- if (git_merge__bases_many(&result, walk, commit, &list) < 0)
- goto on_error;
-
- if (!result) {
- giterr_set(GITERR_MERGE, "No merge base found");
- error = GIT_ENOTFOUND;
- goto on_error;
- }
-
- *out = result;
- *walk_out = walk;
-
- git_vector_free(&list);
- return 0;
-
-on_error:
- git_vector_free(&list);
- git_revwalk_free(walk);
- return error;
-}
-
-int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
-{
- git_revwalk *walk;
- git_commit_list *result = NULL;
- int error = 0;
-
- assert(out && repo && input_array);
-
- if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0)
- return error;
-
- git_oid_cpy(out, &result->item->oid);
-
- git_commit_list_free(&result);
- git_revwalk_free(walk);
-
- return 0;
-}
-
-int git_merge_bases_many(git_oidarray *out, git_repository *repo, size_t length, const git_oid input_array[])
-{
- git_revwalk *walk;
- git_commit_list *list, *result = NULL;
- int error = 0;
- git_array_oid_t array;
-
- assert(out && repo && input_array);
-
- if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0)
- return error;
-
- git_array_init(array);
-
- list = result;
- while (list) {
- git_oid *id = git_array_alloc(array);
- if (id == NULL) {
- error = -1;
- goto cleanup;
- }
-
- git_oid_cpy(id, &list->item->oid);
- list = list->next;
- }
-
- git_oidarray__from_array(out, &array);
-
-cleanup:
- git_commit_list_free(&result);
- git_revwalk_free(walk);
-
- return error;
-}
-
-int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
-{
- git_oid result;
- unsigned int i;
- int error = -1;
-
- assert(out && repo && input_array);
-
- if (length < 2) {
- giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length);
- return -1;
- }
-
- result = input_array[0];
- for (i = 1; i < length; i++) {
- error = git_merge_base(&result, repo, &result, &input_array[i]);
- if (error < 0)
- return error;
- }
-
- *out = result;
-
- return 0;
-}
-
-static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, const git_oid *one, const git_oid *two)
-{
- git_revwalk *walk;
- git_vector list;
- git_commit_list *result = NULL;
- git_commit_list_node *commit;
- void *contents[1];
-
- if (git_revwalk_new(&walk, repo) < 0)
- return -1;
-
- commit = git_revwalk__commit_lookup(walk, two);
- if (commit == NULL)
- goto on_error;
-
- /* This is just one value, so we can do it on the stack */
- memset(&list, 0x0, sizeof(git_vector));
- contents[0] = commit;
- list.length = 1;
- list.contents = contents;
-
- commit = git_revwalk__commit_lookup(walk, one);
- if (commit == NULL)
- goto on_error;
-
- if (git_merge__bases_many(&result, walk, commit, &list) < 0)
- goto on_error;
-
- if (!result) {
- git_revwalk_free(walk);
- giterr_set(GITERR_MERGE, "No merge base found");
- return GIT_ENOTFOUND;
- }
-
- *out = result;
- *walk_out = walk;
-
- return 0;
-
-on_error:
- git_revwalk_free(walk);
- return -1;
-
-}
-
-int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two)
-{
- int error;
- git_revwalk *walk;
- git_commit_list *result;
-
- if ((error = merge_bases(&result, &walk, repo, one, two)) < 0)
- return error;
-
- git_oid_cpy(out, &result->item->oid);
- git_commit_list_free(&result);
- git_revwalk_free(walk);
-
- return 0;
-}
-
-int git_merge_bases(git_oidarray *out, git_repository *repo, const git_oid *one, const git_oid *two)
-{
- int error;
- git_revwalk *walk;
- git_commit_list *result, *list;
- git_array_oid_t array;
-
- git_array_init(array);
-
- if ((error = merge_bases(&result, &walk, repo, one, two)) < 0)
- return error;
-
- list = result;
- while (list) {
- git_oid *id = git_array_alloc(array);
- if (id == NULL)
- goto on_error;
-
- git_oid_cpy(id, &list->item->oid);
- list = list->next;
- }
-
- git_oidarray__from_array(out, &array);
- git_commit_list_free(&result);
- git_revwalk_free(walk);
-
- return 0;
-
-on_error:
- git_commit_list_free(&result);
- git_revwalk_free(walk);
- return -1;
-}
-
-static int interesting(git_pqueue *list)
-{
- size_t i;
-
- for (i = 0; i < git_pqueue_size(list); i++) {
- git_commit_list_node *commit = git_pqueue_get(list, i);
- if ((commit->flags & STALE) == 0)
- return 1;
- }
-
- return 0;
-}
-
-static void clear_commit_marks_1(git_commit_list **plist,
- git_commit_list_node *commit, unsigned int mark)
-{
- while (commit) {
- unsigned int i;
-
- if (!(mark & commit->flags))
- return;
-
- commit->flags &= ~mark;
-
- for (i = 1; i < commit->out_degree; i++) {
- git_commit_list_node *p = commit->parents[i];
- git_commit_list_insert(p, plist);
- }
-
- commit = commit->out_degree ? commit->parents[0] : NULL;
- }
-}
-
-static void clear_commit_marks_many(git_vector *commits, unsigned int mark)
-{
- git_commit_list *list = NULL;
- git_commit_list_node *c;
- unsigned int i;
-
- git_vector_foreach(commits, i, c) {
- git_commit_list_insert(c, &list);
- }
-
- while (list)
- clear_commit_marks_1(&list, git_commit_list_pop(&list), mark);
-}
-
-static void clear_commit_marks(git_commit_list_node *commit, unsigned int mark)
-{
- git_commit_list *list = NULL;
- git_commit_list_insert(commit, &list);
- while (list)
- clear_commit_marks_1(&list, git_commit_list_pop(&list), mark);
-}
-
-static int paint_down_to_common(
- git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
-{
- git_pqueue list;
- git_commit_list *result = NULL;
- git_commit_list_node *two;
-
- int error;
- unsigned int i;
-
- if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0)
- return -1;
-
- one->flags |= PARENT1;
- if (git_pqueue_insert(&list, one) < 0)
- return -1;
-
- git_vector_foreach(twos, i, two) {
- if (git_commit_list_parse(walk, two) < 0)
- return -1;
-
- two->flags |= PARENT2;
-
- if (git_pqueue_insert(&list, two) < 0)
- return -1;
- }
-
- /* as long as there are non-STALE commits */
- while (interesting(&list)) {
- git_commit_list_node *commit = git_pqueue_pop(&list);
- int flags;
-
- if (commit == NULL)
- break;
-
- flags = commit->flags & (PARENT1 | PARENT2 | STALE);
- if (flags == (PARENT1 | PARENT2)) {
- if (!(commit->flags & RESULT)) {
- commit->flags |= RESULT;
- if (git_commit_list_insert(commit, &result) == NULL)
- return -1;
- }
- /* we mark the parents of a merge stale */
- flags |= STALE;
- }
-
- for (i = 0; i < commit->out_degree; i++) {
- git_commit_list_node *p = commit->parents[i];
- if ((p->flags & flags) == flags)
- continue;
-
- if ((error = git_commit_list_parse(walk, p)) < 0)
- return error;
-
- p->flags |= flags;
- if (git_pqueue_insert(&list, p) < 0)
- return -1;
- }
- }
-
- git_pqueue_free(&list);
- *out = result;
- return 0;
-}
-
-static int remove_redundant(git_revwalk *walk, git_vector *commits)
-{
- git_vector work = GIT_VECTOR_INIT;
- unsigned char *redundant;
- unsigned int *filled_index;
- unsigned int i, j;
- int error = 0;
-
- redundant = git__calloc(commits->length, 1);
- GITERR_CHECK_ALLOC(redundant);
- filled_index = git__calloc((commits->length - 1), sizeof(unsigned int));
- GITERR_CHECK_ALLOC(filled_index);
-
- for (i = 0; i < commits->length; ++i) {
- if ((error = git_commit_list_parse(walk, commits->contents[i])) < 0)
- goto done;
- }
-
- for (i = 0; i < commits->length; ++i) {
- git_commit_list *common = NULL;
- git_commit_list_node *commit = commits->contents[i];
-
- if (redundant[i])
- continue;
-
- git_vector_clear(&work);
-
- for (j = 0; j < commits->length; j++) {
- if (i == j || redundant[j])
- continue;
-
- filled_index[work.length] = j;
- if ((error = git_vector_insert(&work, commits->contents[j])) < 0)
- goto done;
- }
-
- error = paint_down_to_common(&common, walk, commit, &work);
- if (error < 0)
- goto done;
-
- if (commit->flags & PARENT2)
- redundant[i] = 1;
-
- for (j = 0; j < work.length; j++) {
- git_commit_list_node *w = work.contents[j];
- if (w->flags & PARENT1)
- redundant[filled_index[j]] = 1;
- }
-
- clear_commit_marks(commit, ALL_FLAGS);
- clear_commit_marks_many(&work, ALL_FLAGS);
-
- git_commit_list_free(&common);
- }
-
- for (i = 0; i < commits->length; ++i) {
- if (redundant[i])
- commits->contents[i] = NULL;
- }
-
-done:
- git__free(redundant);
- git__free(filled_index);
- git_vector_free(&work);
- return error;
-}
-
-int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
-{
- int error;
- unsigned int i;
- git_commit_list_node *two;
- git_commit_list *result = NULL, *tmp = NULL;
-
- /* If there's only the one commit, there can be no merge bases */
- if (twos->length == 0) {
- *out = NULL;
- return 0;
- }
-
- /* if the commit is repeated, we have a our merge base already */
- git_vector_foreach(twos, i, two) {
- if (one == two)
- return git_commit_list_insert(one, out) ? 0 : -1;
- }
-
- if (git_commit_list_parse(walk, one) < 0)
- return -1;
-
- error = paint_down_to_common(&result, walk, one, twos);
- if (error < 0)
- return error;
-
- /* filter out any stale commits in the results */
- tmp = result;
- result = NULL;
-
- while (tmp) {
- git_commit_list_node *c = git_commit_list_pop(&tmp);
- if (!(c->flags & STALE))
- if (git_commit_list_insert_by_date(c, &result) == NULL)
- return -1;
- }
-
- /*
- * more than one merge base -- see if there are redundant merge
- * bases and remove them
- */
- if (result && result->next) {
- git_vector redundant = GIT_VECTOR_INIT;
-
- while (result)
- git_vector_insert(&redundant, git_commit_list_pop(&result));
-
- clear_commit_marks(one, ALL_FLAGS);
- clear_commit_marks_many(twos, ALL_FLAGS);
-
- if ((error = remove_redundant(walk, &redundant)) < 0) {
- git_vector_free(&redundant);
- return error;
- }
-
- git_vector_foreach(&redundant, i, two) {
- if (two != NULL)
- git_commit_list_insert_by_date(two, &result);
- }
-
- git_vector_free(&redundant);
- }
-
- *out = result;
- return 0;
-}
-
-int git_repository_mergehead_foreach(
- git_repository *repo,
- git_repository_mergehead_foreach_cb cb,
- void *payload)
-{
- git_buf merge_head_path = GIT_BUF_INIT, merge_head_file = GIT_BUF_INIT;
- char *buffer, *line;
- size_t line_num = 1;
- git_oid oid;
- int error = 0;
-
- assert(repo && cb);
-
- if ((error = git_buf_joinpath(&merge_head_path, repo->path_repository,
- GIT_MERGE_HEAD_FILE)) < 0)
- return error;
-
- if ((error = git_futils_readbuffer(&merge_head_file,
- git_buf_cstr(&merge_head_path))) < 0)
- goto cleanup;
-
- buffer = merge_head_file.ptr;
-
- while ((line = git__strsep(&buffer, "\n")) != NULL) {
- if (strlen(line) != GIT_OID_HEXSZ) {
- giterr_set(GITERR_INVALID, "Unable to parse OID - invalid length");
- error = -1;
- goto cleanup;
- }
-
- if ((error = git_oid_fromstr(&oid, line)) < 0)
- goto cleanup;
-
- if ((error = cb(&oid, payload)) != 0) {
- giterr_set_after_callback(error);
- goto cleanup;
- }
-
- ++line_num;
- }
-
- if (*buffer) {
- giterr_set(GITERR_MERGE, "No EOL at line %"PRIuZ, line_num);
- error = -1;
- goto cleanup;
- }
-
-cleanup:
- git_buf_free(&merge_head_path);
- git_buf_free(&merge_head_file);
-
- return error;
-}
-
-GIT_INLINE(int) index_entry_cmp(const git_index_entry *a, const git_index_entry *b)
-{
- int value = 0;
-
- if (a->path == NULL)
- return (b->path == NULL) ? 0 : 1;
-
- if ((value = a->mode - b->mode) == 0 &&
- (value = git_oid__cmp(&a->id, &b->id)) == 0)
- value = strcmp(a->path, b->path);
-
- return value;
-}
-
-/* Conflict resolution */
-
-static int merge_conflict_resolve_trivial(
- int *resolved,
- git_merge_diff_list *diff_list,
- const git_merge_diff *conflict)
-{
- int ours_empty, theirs_empty;
- int ours_changed, theirs_changed, ours_theirs_differ;
- git_index_entry const *result = NULL;
- int error = 0;
-
- assert(resolved && diff_list && conflict);
-
- *resolved = 0;
-
- if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE ||
- conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
- return 0;
-
- if (conflict->our_status == GIT_DELTA_RENAMED ||
- conflict->their_status == GIT_DELTA_RENAMED)
- return 0;
-
- ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry);
- theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry);
-
- ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED);
- theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED);
- ours_theirs_differ = ours_changed && theirs_changed &&
- index_entry_cmp(&conflict->our_entry, &conflict->their_entry);
-
- /*
- * Note: with only one ancestor, some cases are not distinct:
- *
- * 16: ancest:anc1/anc2, head:anc1, remote:anc2 = result:no merge
- * 3: ancest:(empty)^, head:head, remote:(empty) = result:no merge
- * 2: ancest:(empty)^, head:(empty), remote:remote = result:no merge
- *
- * Note that the two cases that take D/F conflicts into account
- * specifically do not need to be explicitly tested, as D/F conflicts
- * would fail the *empty* test:
- *
- * 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head
- * 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote
- *
- * Note that many of these cases need not be explicitly tested, as
- * they simply degrade to "all different" cases (eg, 11):
- *
- * 4: ancest:(empty)^, head:head, remote:remote = result:no merge
- * 7: ancest:ancest+, head:(empty), remote:remote = result:no merge
- * 9: ancest:ancest+, head:head, remote:(empty) = result:no merge
- * 11: ancest:ancest+, head:head, remote:remote = result:no merge
- */
-
- /* 5ALT: ancest:*, head:head, remote:head = result:head */
- if (ours_changed && !ours_empty && !ours_theirs_differ)
- result = &conflict->our_entry;
- /* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
- else if (ours_changed && ours_empty && theirs_empty)
- *resolved = 0;
- /* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
- else if (ours_empty && !theirs_changed)
- *resolved = 0;
- /* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
- else if (!ours_changed && theirs_empty)
- *resolved = 0;
- /* 13: ancest:ancest+, head:head, remote:ancest = result:head */
- else if (ours_changed && !theirs_changed)
- result = &conflict->our_entry;
- /* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
- else if (!ours_changed && theirs_changed)
- result = &conflict->their_entry;
- else
- *resolved = 0;
-
- if (result != NULL &&
- GIT_MERGE_INDEX_ENTRY_EXISTS(*result) &&
- (error = git_vector_insert(&diff_list->staged, (void *)result)) >= 0)
- *resolved = 1;
-
- /* Note: trivial resolution does not update the REUC. */
-
- return error;
-}
-
-static int merge_conflict_resolve_one_removed(
- int *resolved,
- git_merge_diff_list *diff_list,
- const git_merge_diff *conflict)
-{
- int ours_empty, theirs_empty;
- int ours_changed, theirs_changed;
- int error = 0;
-
- assert(resolved && diff_list && conflict);
-
- *resolved = 0;
-
- if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE ||
- conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
- return 0;
-
- ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry);
- theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry);
-
- ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED);
- theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED);
-
- /* Removed in both */
- if (ours_changed && ours_empty && theirs_empty)
- *resolved = 1;
- /* Removed in ours */
- else if (ours_empty && !theirs_changed)
- *resolved = 1;
- /* Removed in theirs */
- else if (!ours_changed && theirs_empty)
- *resolved = 1;
-
- if (*resolved)
- git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
-
- return error;
-}
-
-static int merge_conflict_resolve_one_renamed(
- int *resolved,
- git_merge_diff_list *diff_list,
- const git_merge_diff *conflict)
-{
- int ours_renamed, theirs_renamed;
- int ours_changed, theirs_changed;
- git_index_entry *merged;
- int error = 0;
-
- assert(resolved && diff_list && conflict);
-
- *resolved = 0;
-
- if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ||
- !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
- return 0;
-
- ours_renamed = (conflict->our_status == GIT_DELTA_RENAMED);
- theirs_renamed = (conflict->their_status == GIT_DELTA_RENAMED);
-
- if (!ours_renamed && !theirs_renamed)
- return 0;
-
- /* Reject one file in a 2->1 conflict */
- if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 ||
- conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 ||
- conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
- return 0;
-
- ours_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->our_entry.id) != 0);
- theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->their_entry.id) != 0);
-
- /* if both are modified (and not to a common target) require a merge */
- if (ours_changed && theirs_changed &&
- git_oid__cmp(&conflict->our_entry.id, &conflict->their_entry.id) != 0)
- return 0;
-
- if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL)
- return -1;
-
- if (ours_changed)
- memcpy(merged, &conflict->our_entry, sizeof(git_index_entry));
- else
- memcpy(merged, &conflict->their_entry, sizeof(git_index_entry));
-
- if (ours_renamed)
- merged->path = conflict->our_entry.path;
- else
- merged->path = conflict->their_entry.path;
-
- *resolved = 1;
-
- git_vector_insert(&diff_list->staged, merged);
- git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
-
- return error;
-}
-
-static bool merge_conflict_can_resolve_contents(
- const git_merge_diff *conflict)
-{
- if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ||
- !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
- return false;
-
- /* Reject D/F conflicts */
- if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE)
- return false;
-
- /* Reject submodules. */
- if (S_ISGITLINK(conflict->ancestor_entry.mode) ||
- S_ISGITLINK(conflict->our_entry.mode) ||
- S_ISGITLINK(conflict->their_entry.mode))
- return false;
-
- /* Reject link/file conflicts. */
- if ((S_ISLNK(conflict->ancestor_entry.mode) ^
- S_ISLNK(conflict->our_entry.mode)) ||
- (S_ISLNK(conflict->ancestor_entry.mode) ^
- S_ISLNK(conflict->their_entry.mode)))
- return false;
-
- /* Reject name conflicts */
- if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 ||
- conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
- return false;
-
- if ((conflict->our_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED &&
- (conflict->their_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED &&
- strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0)
- return false;
-
- return true;
-}
-
-static int merge_conflict_invoke_driver(
- git_index_entry **out,
- const char *name,
- git_merge_driver *driver,
- git_merge_diff_list *diff_list,
- git_merge_driver_source *src)
-{
- git_index_entry *result;
- git_buf buf = GIT_BUF_INIT;
- const char *path;
- uint32_t mode;
- git_odb *odb = NULL;
- git_oid oid;
- int error;
-
- *out = NULL;
-
- if ((error = driver->apply(driver, &path, &mode, &buf, name, src)) < 0 ||
- (error = git_repository_odb(&odb, src->repo)) < 0 ||
- (error = git_odb_write(&oid, odb, buf.ptr, buf.size, GIT_OBJ_BLOB)) < 0)
- goto done;
-
- result = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry));
- GITERR_CHECK_ALLOC(result);
-
- git_oid_cpy(&result->id, &oid);
- result->mode = mode;
- result->file_size = buf.size;
-
- result->path = git_pool_strdup(&diff_list->pool, path);
- GITERR_CHECK_ALLOC(result->path);
-
- *out = result;
-
-done:
- git_buf_free(&buf);
- git_odb_free(odb);
-
- return error;
-}
-
-static int merge_conflict_resolve_contents(
- int *resolved,
- git_merge_diff_list *diff_list,
- const git_merge_diff *conflict,
- const git_merge_options *merge_opts,
- const git_merge_file_options *file_opts)
-{
- git_merge_driver_source source = {0};
- git_merge_file_result result = {0};
- git_merge_driver *driver;
- git_merge_driver__builtin builtin = {{0}};
- git_index_entry *merge_result;
- git_odb *odb = NULL;
- const char *name;
- bool fallback = false;
- int error;
-
- assert(resolved && diff_list && conflict);
-
- *resolved = 0;
-
- if (!merge_conflict_can_resolve_contents(conflict))
- return 0;
-
- source.repo = diff_list->repo;
- source.default_driver = merge_opts->default_driver;
- source.file_opts = file_opts;
- source.ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
- &conflict->ancestor_entry : NULL;
- source.ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
- &conflict->our_entry : NULL;
- source.theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
- &conflict->their_entry : NULL;
-
- if (file_opts->favor != GIT_MERGE_FILE_FAVOR_NORMAL) {
- /* if the user requested a particular type of resolution (via the
- * favor flag) then let that override the gitattributes and use
- * the builtin driver.
- */
- name = "text";
- builtin.base.apply = git_merge_driver__builtin_apply;
- builtin.favor = file_opts->favor;
-
- driver = &builtin.base;
- } else {
- /* find the merge driver for this file */
- if ((error = git_merge_driver_for_source(&name, &driver, &source)) < 0)
- goto done;
-
- if (driver == NULL)
- fallback = true;
- }
-
- if (driver) {
- error = merge_conflict_invoke_driver(&merge_result, name, driver,
- diff_list, &source);
-
- if (error == GIT_PASSTHROUGH)
- fallback = true;
- }
-
- if (fallback) {
- error = merge_conflict_invoke_driver(&merge_result, "text",
- &git_merge_driver__text.base, diff_list, &source);
- }
-
- if (error < 0) {
- if (error == GIT_EMERGECONFLICT)
- error = 0;
-
- goto done;
- }
-
- git_vector_insert(&diff_list->staged, merge_result);
- git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
-
- *resolved = 1;
-
-done:
- git_merge_file_result_free(&result);
- git_odb_free(odb);
-
- return error;
-}
-
-static int merge_conflict_resolve(
- int *out,
- git_merge_diff_list *diff_list,
- const git_merge_diff *conflict,
- const git_merge_options *merge_opts,
- const git_merge_file_options *file_opts)
-{
- int resolved = 0;
- int error = 0;
-
- *out = 0;
-
- if ((error = merge_conflict_resolve_trivial(
- &resolved, diff_list, conflict)) < 0)
- goto done;
-
- if (!resolved && (error = merge_conflict_resolve_one_removed(
- &resolved, diff_list, conflict)) < 0)
- goto done;
-
- if (!resolved && (error = merge_conflict_resolve_one_renamed(
- &resolved, diff_list, conflict)) < 0)
- goto done;
-
- if (!resolved && (error = merge_conflict_resolve_contents(
- &resolved, diff_list, conflict, merge_opts, file_opts)) < 0)
- goto done;
-
- *out = resolved;
-
-done:
- return error;
-}
-
-/* Rename detection and coalescing */
-
-struct merge_diff_similarity {
- unsigned char similarity;
- size_t other_idx;
-};
-
-static int index_entry_similarity_exact(
- git_repository *repo,
- git_index_entry *a,
- size_t a_idx,
- git_index_entry *b,
- size_t b_idx,
- void **cache,
- const git_merge_options *opts)
-{
- GIT_UNUSED(repo);
- GIT_UNUSED(a_idx);
- GIT_UNUSED(b_idx);
- GIT_UNUSED(cache);
- GIT_UNUSED(opts);
-
- if (git_oid__cmp(&a->id, &b->id) == 0)
- return 100;
-
- return 0;
-}
-
-static int index_entry_similarity_calc(
- void **out,
- git_repository *repo,
- git_index_entry *entry,
- const git_merge_options *opts)
-{
- git_blob *blob;
- git_diff_file diff_file = {{{0}}};
- git_off_t blobsize;
- int error;
-
- *out = NULL;
-
- if ((error = git_blob_lookup(&blob, repo, &entry->id)) < 0)
- return error;
-
- git_oid_cpy(&diff_file.id, &entry->id);
- diff_file.path = entry->path;
- diff_file.size = entry->file_size;
- diff_file.mode = entry->mode;
- diff_file.flags = 0;
-
- blobsize = git_blob_rawsize(blob);
-
- /* file too big for rename processing */
- if (!git__is_sizet(blobsize))
- return 0;
-
- error = opts->metric->buffer_signature(out, &diff_file,
- git_blob_rawcontent(blob), (size_t)blobsize,
- opts->metric->payload);
-
- git_blob_free(blob);
-
- return error;
-}
-
-static int index_entry_similarity_inexact(
- git_repository *repo,
- git_index_entry *a,
- size_t a_idx,
- git_index_entry *b,
- size_t b_idx,
- void **cache,
- const git_merge_options *opts)
-{
- int score = 0;
- int error = 0;
-
- if (GIT_MODE_TYPE(a->mode) != GIT_MODE_TYPE(b->mode))
- return 0;
-
- /* update signature cache if needed */
- if (!cache[a_idx] && (error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0)
- return error;
- if (!cache[b_idx] && (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0)
- return error;
-
- /* some metrics may not wish to process this file (too big / too small) */
- if (!cache[a_idx] || !cache[b_idx])
- return 0;
-
- /* compare signatures */
- if (opts->metric->similarity(
- &score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
- return -1;
-
- /* clip score */
- if (score < 0)
- score = 0;
- else if (score > 100)
- score = 100;
-
- return score;
-}
-
-static int merge_diff_mark_similarity(
- git_repository *repo,
- git_merge_diff_list *diff_list,
- struct merge_diff_similarity *similarity_ours,
- struct merge_diff_similarity *similarity_theirs,
- int (*similarity_fn)(git_repository *, git_index_entry *, size_t, git_index_entry *, size_t, void **, const git_merge_options *),
- void **cache,
- const git_merge_options *opts)
-{
- size_t i, j;
- git_merge_diff *conflict_src, *conflict_tgt;
- int similarity;
-
- git_vector_foreach(&diff_list->conflicts, i, conflict_src) {
- /* Items can be the source of a rename iff they have an item in the
- * ancestor slot and lack an item in the ours or theirs slot. */
- if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry) ||
- (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry) &&
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)))
- continue;
-
- git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) {
- size_t our_idx = diff_list->conflicts.length + j;
- size_t their_idx = (diff_list->conflicts.length * 2) + j;
-
- if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry))
- continue;
-
- if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) &&
- !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
- similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts);
-
- if (similarity == GIT_EBUFS)
- continue;
- else if (similarity < 0)
- return similarity;
-
- if (similarity > similarity_ours[i].similarity &&
- similarity > similarity_ours[j].similarity) {
- /* Clear previous best similarity */
- if (similarity_ours[i].similarity > 0)
- similarity_ours[similarity_ours[i].other_idx].similarity = 0;
-
- if (similarity_ours[j].similarity > 0)
- similarity_ours[similarity_ours[j].other_idx].similarity = 0;
-
- similarity_ours[i].similarity = similarity;
- similarity_ours[i].other_idx = j;
-
- similarity_ours[j].similarity = similarity;
- similarity_ours[j].other_idx = i;
- }
- }
-
- if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) &&
- !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) {
- similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts);
-
- if (similarity > similarity_theirs[i].similarity &&
- similarity > similarity_theirs[j].similarity) {
- /* Clear previous best similarity */
- if (similarity_theirs[i].similarity > 0)
- similarity_theirs[similarity_theirs[i].other_idx].similarity = 0;
-
- if (similarity_theirs[j].similarity > 0)
- similarity_theirs[similarity_theirs[j].other_idx].similarity = 0;
-
- similarity_theirs[i].similarity = similarity;
- similarity_theirs[i].other_idx = j;
-
- similarity_theirs[j].similarity = similarity;
- similarity_theirs[j].other_idx = i;
- }
- }
- }
- }
-
- return 0;
-}
-
-/*
- * Rename conflicts:
- *
- * Ancestor Ours Theirs
- *
- * 0a A A A No rename
- * b A A* A No rename (ours was rewritten)
- * c A A A* No rename (theirs rewritten)
- * 1a A A B[A] Rename or rename/edit
- * b A B[A] A (automergeable)
- * 2 A B[A] B[A] Both renamed (automergeable)
- * 3a A B[A] Rename/delete
- * b A B[A] (same)
- * 4a A B[A] B Rename/add [B~ours B~theirs]
- * b A B B[A] (same)
- * 5 A B[A] C[A] Both renamed ("1 -> 2")
- * 6 A C[A] Both renamed ("2 -> 1")
- * B C[B] [C~ours C~theirs] (automergeable)
- */
-static void merge_diff_mark_rename_conflict(
- git_merge_diff_list *diff_list,
- struct merge_diff_similarity *similarity_ours,
- bool ours_renamed,
- size_t ours_source_idx,
- struct merge_diff_similarity *similarity_theirs,
- bool theirs_renamed,
- size_t theirs_source_idx,
- git_merge_diff *target,
- const git_merge_options *opts)
-{
- git_merge_diff *ours_source = NULL, *theirs_source = NULL;
-
- if (ours_renamed)
- ours_source = diff_list->conflicts.contents[ours_source_idx];
-
- if (theirs_renamed)
- theirs_source = diff_list->conflicts.contents[theirs_source_idx];
-
- /* Detect 2->1 conflicts */
- if (ours_renamed && theirs_renamed) {
- /* Both renamed to the same target name. */
- if (ours_source_idx == theirs_source_idx)
- ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED;
- else {
- ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1;
- theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1;
- }
- } else if (ours_renamed) {
- /* If our source was also renamed in theirs, this is a 1->2 */
- if (similarity_theirs[ours_source_idx].similarity >= opts->rename_threshold)
- ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2;
-
- else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry)) {
- ours_source->type = GIT_MERGE_DIFF_RENAMED_ADDED;
- target->type = GIT_MERGE_DIFF_RENAMED_ADDED;
- }
-
- else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(ours_source->their_entry))
- ours_source->type = GIT_MERGE_DIFF_RENAMED_DELETED;
-
- else if (ours_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED)
- ours_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED;
- } else if (theirs_renamed) {
- /* If their source was also renamed in ours, this is a 1->2 */
- if (similarity_ours[theirs_source_idx].similarity >= opts->rename_threshold)
- theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2;
-
- else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry)) {
- theirs_source->type = GIT_MERGE_DIFF_RENAMED_ADDED;
- target->type = GIT_MERGE_DIFF_RENAMED_ADDED;
- }
-
- else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(theirs_source->our_entry))
- theirs_source->type = GIT_MERGE_DIFF_RENAMED_DELETED;
-
- else if (theirs_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED)
- theirs_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED;
- }
-}
-
-GIT_INLINE(void) merge_diff_coalesce_rename(
- git_index_entry *source_entry,
- git_delta_t *source_status,
- git_index_entry *target_entry,
- git_delta_t *target_status)
-{
- /* Coalesce the rename target into the rename source. */
- memcpy(source_entry, target_entry, sizeof(git_index_entry));
- *source_status = GIT_DELTA_RENAMED;
-
- memset(target_entry, 0x0, sizeof(git_index_entry));
- *target_status = GIT_DELTA_UNMODIFIED;
-}
-
-static void merge_diff_list_coalesce_renames(
- git_merge_diff_list *diff_list,
- struct merge_diff_similarity *similarity_ours,
- struct merge_diff_similarity *similarity_theirs,
- const git_merge_options *opts)
-{
- size_t i;
- bool ours_renamed = 0, theirs_renamed = 0;
- size_t ours_source_idx = 0, theirs_source_idx = 0;
- git_merge_diff *ours_source, *theirs_source, *target;
-
- for (i = 0; i < diff_list->conflicts.length; i++) {
- target = diff_list->conflicts.contents[i];
-
- ours_renamed = 0;
- theirs_renamed = 0;
-
- if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry) &&
- similarity_ours[i].similarity >= opts->rename_threshold) {
- ours_source_idx = similarity_ours[i].other_idx;
-
- ours_source = diff_list->conflicts.contents[ours_source_idx];
-
- merge_diff_coalesce_rename(
- &ours_source->our_entry,
- &ours_source->our_status,
- &target->our_entry,
- &target->our_status);
-
- similarity_ours[ours_source_idx].similarity = 0;
- similarity_ours[i].similarity = 0;
-
- ours_renamed = 1;
- }
-
- /* insufficient to determine direction */
- if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry) &&
- similarity_theirs[i].similarity >= opts->rename_threshold) {
- theirs_source_idx = similarity_theirs[i].other_idx;
-
- theirs_source = diff_list->conflicts.contents[theirs_source_idx];
-
- merge_diff_coalesce_rename(
- &theirs_source->their_entry,
- &theirs_source->their_status,
- &target->their_entry,
- &target->their_status);
-
- similarity_theirs[theirs_source_idx].similarity = 0;
- similarity_theirs[i].similarity = 0;
-
- theirs_renamed = 1;
- }
-
- merge_diff_mark_rename_conflict(diff_list,
- similarity_ours, ours_renamed, ours_source_idx,
- similarity_theirs, theirs_renamed, theirs_source_idx,
- target, opts);
- }
-}
-
-static int merge_diff_empty(const git_vector *conflicts, size_t idx, void *p)
-{
- git_merge_diff *conflict = conflicts->contents[idx];
-
- GIT_UNUSED(p);
-
- return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) &&
- !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) &&
- !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry));
-}
-
-static void merge_diff_list_count_candidates(
- git_merge_diff_list *diff_list,
- size_t *src_count,
- size_t *tgt_count)
-{
- git_merge_diff *entry;
- size_t i;
-
- *src_count = 0;
- *tgt_count = 0;
-
- git_vector_foreach(&diff_list->conflicts, i, entry) {
- if (GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry) &&
- (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->our_entry) ||
- !GIT_MERGE_INDEX_ENTRY_EXISTS(entry->their_entry)))
- (*src_count)++;
- else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry))
- (*tgt_count)++;
- }
-}
-
-int git_merge_diff_list__find_renames(
- git_repository *repo,
- git_merge_diff_list *diff_list,
- const git_merge_options *opts)
-{
- struct merge_diff_similarity *similarity_ours, *similarity_theirs;
- void **cache = NULL;
- size_t cache_size = 0;
- size_t src_count, tgt_count, i;
- int error = 0;
-
- assert(diff_list && opts);
-
- if ((opts->flags & GIT_MERGE_FIND_RENAMES) == 0)
- return 0;
-
- similarity_ours = git__calloc(diff_list->conflicts.length,
- sizeof(struct merge_diff_similarity));
- GITERR_CHECK_ALLOC(similarity_ours);
-
- similarity_theirs = git__calloc(diff_list->conflicts.length,
- sizeof(struct merge_diff_similarity));
- GITERR_CHECK_ALLOC(similarity_theirs);
-
- /* Calculate similarity between items that were deleted from the ancestor
- * and added in the other branch.
- */
- if ((error = merge_diff_mark_similarity(repo, diff_list, similarity_ours,
- similarity_theirs, index_entry_similarity_exact, NULL, opts)) < 0)
- goto done;
-
- if (diff_list->conflicts.length <= opts->target_limit) {
- GITERR_CHECK_ALLOC_MULTIPLY(&cache_size, diff_list->conflicts.length, 3);
- cache = git__calloc(cache_size, sizeof(void *));
- GITERR_CHECK_ALLOC(cache);
-
- merge_diff_list_count_candidates(diff_list, &src_count, &tgt_count);
-
- if (src_count > opts->target_limit || tgt_count > opts->target_limit) {
- /* TODO: report! */
- } else {
- if ((error = merge_diff_mark_similarity(
- repo, diff_list, similarity_ours, similarity_theirs,
- index_entry_similarity_inexact, cache, opts)) < 0)
- goto done;
- }
- }
-
- /* For entries that are appropriately similar, merge the new name's entry
- * into the old name.
- */
- merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts);
-
- /* And remove any entries that were merged and are now empty. */
- git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty, NULL);
-
-done:
- if (cache != NULL) {
- for (i = 0; i < cache_size; ++i) {
- if (cache[i] != NULL)
- opts->metric->free_signature(cache[i], opts->metric->payload);
- }
-
- git__free(cache);
- }
-
- git__free(similarity_ours);
- git__free(similarity_theirs);
-
- return error;
-}
-
-/* Directory/file conflict handling */
-
-GIT_INLINE(const char *) merge_diff_path(
- const git_merge_diff *conflict)
-{
- if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry))
- return conflict->ancestor_entry.path;
- else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry))
- return conflict->our_entry.path;
- else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
- return conflict->their_entry.path;
-
- return NULL;
-}
-
-GIT_INLINE(bool) merge_diff_any_side_added_or_modified(
- const git_merge_diff *conflict)
-{
- if (conflict->our_status == GIT_DELTA_ADDED ||
- conflict->our_status == GIT_DELTA_MODIFIED ||
- conflict->their_status == GIT_DELTA_ADDED ||
- conflict->their_status == GIT_DELTA_MODIFIED)
- return true;
-
- return false;
-}
-
-GIT_INLINE(bool) path_is_prefixed(const char *parent, const char *child)
-{
- size_t child_len = strlen(child);
- size_t parent_len = strlen(parent);
-
- if (child_len < parent_len ||
- strncmp(parent, child, parent_len) != 0)
- return 0;
-
- return (child[parent_len] == '/');
-}
-
-GIT_INLINE(int) merge_diff_detect_df_conflict(
- struct merge_diff_df_data *df_data,
- git_merge_diff *conflict)
-{
- const char *cur_path = merge_diff_path(conflict);
-
- /* Determine if this is a D/F conflict or the child of one */
- if (df_data->df_path &&
- path_is_prefixed(df_data->df_path, cur_path))
- conflict->type = GIT_MERGE_DIFF_DF_CHILD;
- else if(df_data->df_path)
- df_data->df_path = NULL;
- else if (df_data->prev_path &&
- merge_diff_any_side_added_or_modified(df_data->prev_conflict) &&
- merge_diff_any_side_added_or_modified(conflict) &&
- path_is_prefixed(df_data->prev_path, cur_path)) {
- conflict->type = GIT_MERGE_DIFF_DF_CHILD;
-
- df_data->prev_conflict->type = GIT_MERGE_DIFF_DIRECTORY_FILE;
- df_data->df_path = df_data->prev_path;
- }
-
- df_data->prev_path = cur_path;
- df_data->prev_conflict = conflict;
-
- return 0;
-}
-
-/* Conflict handling */
-
-GIT_INLINE(int) merge_diff_detect_type(
- git_merge_diff *conflict)
-{
- if (conflict->our_status == GIT_DELTA_ADDED &&
- conflict->their_status == GIT_DELTA_ADDED)
- conflict->type = GIT_MERGE_DIFF_BOTH_ADDED;
- else if (conflict->our_status == GIT_DELTA_MODIFIED &&
- conflict->their_status == GIT_DELTA_MODIFIED)
- conflict->type = GIT_MERGE_DIFF_BOTH_MODIFIED;
- else if (conflict->our_status == GIT_DELTA_DELETED &&
- conflict->their_status == GIT_DELTA_DELETED)
- conflict->type = GIT_MERGE_DIFF_BOTH_DELETED;
- else if (conflict->our_status == GIT_DELTA_MODIFIED &&
- conflict->their_status == GIT_DELTA_DELETED)
- conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED;
- else if (conflict->our_status == GIT_DELTA_DELETED &&
- conflict->their_status == GIT_DELTA_MODIFIED)
- conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED;
- else
- conflict->type = GIT_MERGE_DIFF_NONE;
-
- return 0;
-}
-
-GIT_INLINE(int) index_entry_dup_pool(
- git_index_entry *out,
- git_pool *pool,
- const git_index_entry *src)
-{
- if (src != NULL) {
- memcpy(out, src, sizeof(git_index_entry));
- if ((out->path = git_pool_strdup(pool, src->path)) == NULL)
- return -1;
- }
-
- return 0;
-}
-
-GIT_INLINE(int) merge_delta_type_from_index_entries(
- const git_index_entry *ancestor,
- const git_index_entry *other)
-{
- if (ancestor == NULL && other == NULL)
- return GIT_DELTA_UNMODIFIED;
- else if (ancestor == NULL && other != NULL)
- return GIT_DELTA_ADDED;
- else if (ancestor != NULL && other == NULL)
- return GIT_DELTA_DELETED;
- else if (S_ISDIR(ancestor->mode) ^ S_ISDIR(other->mode))
- return GIT_DELTA_TYPECHANGE;
- else if(S_ISLNK(ancestor->mode) ^ S_ISLNK(other->mode))
- return GIT_DELTA_TYPECHANGE;
- else if (git_oid__cmp(&ancestor->id, &other->id) ||
- ancestor->mode != other->mode)
- return GIT_DELTA_MODIFIED;
-
- return GIT_DELTA_UNMODIFIED;
-}
-
-static git_merge_diff *merge_diff_from_index_entries(
- git_merge_diff_list *diff_list,
- const git_index_entry **entries)
-{
- git_merge_diff *conflict;
- git_pool *pool = &diff_list->pool;
-
- if ((conflict = git_pool_mallocz(pool, sizeof(git_merge_diff))) == NULL)
- return NULL;
-
- if (index_entry_dup_pool(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 ||
- index_entry_dup_pool(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 ||
- index_entry_dup_pool(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0)
- return NULL;
-
- conflict->our_status = merge_delta_type_from_index_entries(
- entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_OURS]);
- conflict->their_status = merge_delta_type_from_index_entries(
- entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_THEIRS]);
-
- return conflict;
-}
-
-/* Merge trees */
-
-static int merge_diff_list_insert_conflict(
- git_merge_diff_list *diff_list,
- struct merge_diff_df_data *merge_df_data,
- const git_index_entry *tree_items[3])
-{
- git_merge_diff *conflict;
-
- if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL ||
- merge_diff_detect_type(conflict) < 0 ||
- merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 ||
- git_vector_insert(&diff_list->conflicts, conflict) < 0)
- return -1;
-
- return 0;
-}
-
-static int merge_diff_list_insert_unmodified(
- git_merge_diff_list *diff_list,
- const git_index_entry *tree_items[3])
-{
- int error = 0;
- git_index_entry *entry;
-
- entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry));
- GITERR_CHECK_ALLOC(entry);
-
- if ((error = index_entry_dup_pool(entry, &diff_list->pool, tree_items[0])) >= 0)
- error = git_vector_insert(&diff_list->staged, entry);
-
- return error;
-}
-
-struct merge_diff_find_data {
- git_merge_diff_list *diff_list;
- struct merge_diff_df_data df_data;
-};
-
-static int queue_difference(const git_index_entry **entries, void *data)
-{
- struct merge_diff_find_data *find_data = data;
- bool item_modified = false;
- size_t i;
-
- if (!entries[0] || !entries[1] || !entries[2]) {
- item_modified = true;
- } else {
- for (i = 1; i < 3; i++) {
- if (index_entry_cmp(entries[0], entries[i]) != 0) {
- item_modified = true;
- break;
- }
- }
- }
-
- return item_modified ?
- merge_diff_list_insert_conflict(
- find_data->diff_list, &find_data->df_data, entries) :
- merge_diff_list_insert_unmodified(find_data->diff_list, entries);
-}
-
-int git_merge_diff_list__find_differences(
- git_merge_diff_list *diff_list,
- git_iterator *ancestor_iter,
- git_iterator *our_iter,
- git_iterator *their_iter)
-{
- git_iterator *iterators[3] = { ancestor_iter, our_iter, their_iter };
- struct merge_diff_find_data find_data = { diff_list };
-
- return git_iterator_walk(iterators, 3, queue_difference, &find_data);
-}
-
-git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo)
-{
- git_merge_diff_list *diff_list = git__calloc(1, sizeof(git_merge_diff_list));
-
- if (diff_list == NULL)
- return NULL;
-
- diff_list->repo = repo;
-
- git_pool_init(&diff_list->pool, 1);
-
- if (git_vector_init(&diff_list->staged, 0, NULL) < 0 ||
- git_vector_init(&diff_list->conflicts, 0, NULL) < 0 ||
- git_vector_init(&diff_list->resolved, 0, NULL) < 0) {
- git_merge_diff_list__free(diff_list);
- return NULL;
- }
-
- return diff_list;
-}
-
-void git_merge_diff_list__free(git_merge_diff_list *diff_list)
-{
- if (!diff_list)
- return;
-
- git_vector_free(&diff_list->staged);
- git_vector_free(&diff_list->conflicts);
- git_vector_free(&diff_list->resolved);
- git_pool_clear(&diff_list->pool);
- git__free(diff_list);
-}
-
-static int merge_normalize_opts(
- git_repository *repo,
- git_merge_options *opts,
- const git_merge_options *given)
-{
- git_config *cfg = NULL;
- git_config_entry *entry = NULL;
- int error = 0;
-
- assert(repo && opts);
-
- if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
- return error;
-
- if (given != NULL)
- memcpy(opts, given, sizeof(git_merge_options));
- else {
- git_merge_options init = GIT_MERGE_OPTIONS_INIT;
- memcpy(opts, &init, sizeof(init));
-
- opts->flags = GIT_MERGE_FIND_RENAMES;
- opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD;
- }
-
- if (given && given->default_driver) {
- opts->default_driver = git__strdup(given->default_driver);
- GITERR_CHECK_ALLOC(opts->default_driver);
- } else {
- error = git_config_get_entry(&entry, cfg, "merge.default");
-
- if (error == 0) {
- opts->default_driver = git__strdup(entry->value);
- GITERR_CHECK_ALLOC(opts->default_driver);
- } else if (error == GIT_ENOTFOUND) {
- error = 0;
- } else {
- goto done;
- }
- }
-
- if (!opts->target_limit) {
- int limit = git_config__get_int_force(cfg, "merge.renamelimit", 0);
-
- if (!limit)
- limit = git_config__get_int_force(cfg, "diff.renamelimit", 0);
-
- opts->target_limit = (limit <= 0) ?
- GIT_MERGE_DEFAULT_TARGET_LIMIT : (unsigned int)limit;
- }
-
- /* assign the internal metric with whitespace flag as payload */
- if (!opts->metric) {
- opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
- GITERR_CHECK_ALLOC(opts->metric);
-
- opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
- opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
- opts->metric->free_signature = git_diff_find_similar__hashsig_free;
- opts->metric->similarity = git_diff_find_similar__calc_similarity;
- opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE;
- }
-
-done:
- git_config_entry_free(entry);
- return error;
-}
-
-
-static int merge_index_insert_reuc(
- git_index *index,
- size_t idx,
- const git_index_entry *entry)
-{
- const git_index_reuc_entry *reuc;
- int mode[3] = { 0, 0, 0 };
- git_oid const *oid[3] = { NULL, NULL, NULL };
- size_t i;
-
- if (!GIT_MERGE_INDEX_ENTRY_EXISTS(*entry))
- return 0;
-
- if ((reuc = git_index_reuc_get_bypath(index, entry->path)) != NULL) {
- for (i = 0; i < 3; i++) {
- mode[i] = reuc->mode[i];
- oid[i] = &reuc->oid[i];
- }
- }
-
- mode[idx] = entry->mode;
- oid[idx] = &entry->id;
-
- return git_index_reuc_add(index, entry->path,
- mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]);
-}
-
-static int index_update_reuc(git_index *index, git_merge_diff_list *diff_list)
-{
- int error;
- size_t i;
- git_merge_diff *conflict;
-
- /* Add each entry in the resolved conflict to the REUC independently, since
- * the paths may differ due to renames. */
- git_vector_foreach(&diff_list->resolved, i, conflict) {
- const git_index_entry *ancestor =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
- &conflict->ancestor_entry : NULL;
-
- const git_index_entry *ours =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
- &conflict->our_entry : NULL;
-
- const git_index_entry *theirs =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
- &conflict->their_entry : NULL;
-
- if (ancestor != NULL &&
- (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0)
- return error;
-
- if (ours != NULL &&
- (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0)
- return error;
-
- if (theirs != NULL &&
- (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0)
- return error;
- }
-
- return 0;
-}
-
-static int index_from_diff_list(git_index **out,
- git_merge_diff_list *diff_list, bool skip_reuc)
-{
- git_index *index;
- size_t i;
- git_merge_diff *conflict;
- int error = 0;
-
- *out = NULL;
-
- if ((error = git_index_new(&index)) < 0)
- return error;
-
- if ((error = git_index__fill(index, &diff_list->staged)) < 0)
- goto on_error;
-
- git_vector_foreach(&diff_list->conflicts, i, conflict) {
- const git_index_entry *ancestor =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
- &conflict->ancestor_entry : NULL;
-
- const git_index_entry *ours =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
- &conflict->our_entry : NULL;
-
- const git_index_entry *theirs =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
- &conflict->their_entry : NULL;
-
- if ((error = git_index_conflict_add(index, ancestor, ours, theirs)) < 0)
- goto on_error;
- }
-
- /* Add each rename entry to the rename portion of the index. */
- git_vector_foreach(&diff_list->conflicts, i, conflict) {
- const char *ancestor_path, *our_path, *their_path;
-
- if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry))
- continue;
-
- ancestor_path = conflict->ancestor_entry.path;
-
- our_path =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
- conflict->our_entry.path : NULL;
-
- their_path =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
- conflict->their_entry.path : NULL;
-
- if ((our_path && strcmp(ancestor_path, our_path) != 0) ||
- (their_path && strcmp(ancestor_path, their_path) != 0)) {
- if ((error = git_index_name_add(index, ancestor_path, our_path, their_path)) < 0)
- goto on_error;
- }
- }
-
- if (!skip_reuc) {
- if ((error = index_update_reuc(index, diff_list)) < 0)
- goto on_error;
- }
-
- *out = index;
- return 0;
-
-on_error:
- git_index_free(index);
- return error;
-}
-
-static git_iterator *iterator_given_or_empty(git_iterator **empty, git_iterator *given)
-{
- git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
-
- if (given)
- return given;
-
- opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
-
- if (git_iterator_for_nothing(empty, &opts) < 0)
- return NULL;
-
- return *empty;
-}
-
-int git_merge__iterators(
- git_index **out,
- git_repository *repo,
- git_iterator *ancestor_iter,
- git_iterator *our_iter,
- git_iterator *theirs_iter,
- const git_merge_options *given_opts)
-{
- git_iterator *empty_ancestor = NULL,
- *empty_ours = NULL,
- *empty_theirs = NULL;
- git_merge_diff_list *diff_list;
- git_merge_options opts;
- git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
- git_merge_diff *conflict;
- git_vector changes;
- size_t i;
- int error = 0;
-
- assert(out && repo);
-
- *out = NULL;
-
- GITERR_CHECK_VERSION(
- given_opts, GIT_MERGE_OPTIONS_VERSION, "git_merge_options");
-
- if ((error = merge_normalize_opts(repo, &opts, given_opts)) < 0)
- return error;
-
- file_opts.favor = opts.file_favor;
- file_opts.flags = opts.file_flags;
-
- /* use the git-inspired labels when virtual base building */
- if (opts.flags & GIT_MERGE__VIRTUAL_BASE) {
- file_opts.ancestor_label = "merged common ancestors";
- file_opts.our_label = "Temporary merge branch 1";
- file_opts.their_label = "Temporary merge branch 2";
- file_opts.flags |= GIT_MERGE_FILE_FAVOR__CONFLICTED;
- }
-
- diff_list = git_merge_diff_list__alloc(repo);
- GITERR_CHECK_ALLOC(diff_list);
-
- ancestor_iter = iterator_given_or_empty(&empty_ancestor, ancestor_iter);
- our_iter = iterator_given_or_empty(&empty_ours, our_iter);
- theirs_iter = iterator_given_or_empty(&empty_theirs, theirs_iter);
-
- if ((error = git_merge_diff_list__find_differences(
- diff_list, ancestor_iter, our_iter, theirs_iter)) < 0 ||
- (error = git_merge_diff_list__find_renames(repo, diff_list, &opts)) < 0)
- goto done;
-
- memcpy(&changes, &diff_list->conflicts, sizeof(git_vector));
- git_vector_clear(&diff_list->conflicts);
-
- git_vector_foreach(&changes, i, conflict) {
- int resolved = 0;
-
- if ((error = merge_conflict_resolve(
- &resolved, diff_list, conflict, &opts, &file_opts)) < 0)
- goto done;
-
- if (!resolved) {
- if ((opts.flags & GIT_MERGE_FAIL_ON_CONFLICT)) {
- giterr_set(GITERR_MERGE, "merge conflicts exist");
- error = GIT_EMERGECONFLICT;
- goto done;
- }
-
- git_vector_insert(&diff_list->conflicts, conflict);
- }
- }
-
- error = index_from_diff_list(out, diff_list,
- (opts.flags & GIT_MERGE_SKIP_REUC));
-
-done:
- if (!given_opts || !given_opts->metric)
- git__free(opts.metric);
-
- git__free((char *)opts.default_driver);
-
- git_merge_diff_list__free(diff_list);
- git_iterator_free(empty_ancestor);
- git_iterator_free(empty_ours);
- git_iterator_free(empty_theirs);
-
- return error;
-}
-
-int git_merge_trees(
- git_index **out,
- git_repository *repo,
- const git_tree *ancestor_tree,
- const git_tree *our_tree,
- const git_tree *their_tree,
- const git_merge_options *merge_opts)
-{
- git_iterator *ancestor_iter = NULL, *our_iter = NULL, *their_iter = NULL;
- git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
- int error;
-
- iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
-
- if ((error = git_iterator_for_tree(
- &ancestor_iter, (git_tree *)ancestor_tree, &iter_opts)) < 0 ||
- (error = git_iterator_for_tree(
- &our_iter, (git_tree *)our_tree, &iter_opts)) < 0 ||
- (error = git_iterator_for_tree(
- &their_iter, (git_tree *)their_tree, &iter_opts)) < 0)
- goto done;
-
- error = git_merge__iterators(
- out, repo, ancestor_iter, our_iter, their_iter, merge_opts);
-
-done:
- git_iterator_free(ancestor_iter);
- git_iterator_free(our_iter);
- git_iterator_free(their_iter);
-
- return error;
-}
-
-static int merge_annotated_commits(
- git_index **index_out,
- git_annotated_commit **base_out,
- git_repository *repo,
- git_annotated_commit *our_commit,
- git_annotated_commit *their_commit,
- size_t recursion_level,
- const git_merge_options *opts);
-
-GIT_INLINE(int) insert_head_ids(
- git_array_oid_t *ids,
- const git_annotated_commit *annotated_commit)
-{
- git_oid *id;
- size_t i;
-
- if (annotated_commit->type == GIT_ANNOTATED_COMMIT_REAL) {
- id = git_array_alloc(*ids);
- GITERR_CHECK_ALLOC(id);
-
- git_oid_cpy(id, git_commit_id(annotated_commit->commit));
- } else {
- for (i = 0; i < annotated_commit->parents.size; i++) {
- id = git_array_alloc(*ids);
- GITERR_CHECK_ALLOC(id);
-
- git_oid_cpy(id, &annotated_commit->parents.ptr[i]);
- }
- }
-
- return 0;
-}
-
-static int create_virtual_base(
- git_annotated_commit **out,
- git_repository *repo,
- git_annotated_commit *one,
- git_annotated_commit *two,
- const git_merge_options *opts,
- size_t recursion_level)
-{
- git_annotated_commit *result = NULL;
- git_index *index = NULL;
- git_merge_options virtual_opts = GIT_MERGE_OPTIONS_INIT;
-
- /* Conflicts in the merge base creation do not propagate to conflicts
- * in the result; the conflicted base will act as the common ancestor.
- */
- if (opts)
- memcpy(&virtual_opts, opts, sizeof(git_merge_options));
-
- virtual_opts.flags &= ~GIT_MERGE_FAIL_ON_CONFLICT;
- virtual_opts.flags |= GIT_MERGE__VIRTUAL_BASE;
-
- if ((merge_annotated_commits(&index, NULL, repo, one, two,
- recursion_level + 1, &virtual_opts)) < 0)
- return -1;
-
- result = git__calloc(1, sizeof(git_annotated_commit));
- GITERR_CHECK_ALLOC(result);
- result->type = GIT_ANNOTATED_COMMIT_VIRTUAL;
- result->index = index;
-
- insert_head_ids(&result->parents, one);
- insert_head_ids(&result->parents, two);
-
- *out = result;
- return 0;
-}
-
-static int compute_base(
- git_annotated_commit **out,
- git_repository *repo,
- const git_annotated_commit *one,
- const git_annotated_commit *two,
- const git_merge_options *given_opts,
- size_t recursion_level)
-{
- git_array_oid_t head_ids = GIT_ARRAY_INIT;
- git_oidarray bases = {0};
- git_annotated_commit *base = NULL, *other = NULL, *new_base = NULL;
- git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
- size_t i;
- int error;
-
- *out = NULL;
-
- if (given_opts)
- memcpy(&opts, given_opts, sizeof(git_merge_options));
-
- if ((error = insert_head_ids(&head_ids, one)) < 0 ||
- (error = insert_head_ids(&head_ids, two)) < 0)
- goto done;
-
- if ((error = git_merge_bases_many(&bases, repo,
- head_ids.size, head_ids.ptr)) < 0 ||
- (error = git_annotated_commit_lookup(&base, repo, &bases.ids[0])) < 0 ||
- (opts.flags & GIT_MERGE_NO_RECURSIVE))
- goto done;
-
- for (i = 1; i < bases.count; i++) {
- recursion_level++;
-
- if (opts.recursion_limit && recursion_level > opts.recursion_limit)
- break;
-
- if ((error = git_annotated_commit_lookup(&other, repo,
- &bases.ids[i])) < 0 ||
- (error = create_virtual_base(&new_base, repo, base, other, &opts,
- recursion_level)) < 0)
- goto done;
-
- git_annotated_commit_free(base);
- git_annotated_commit_free(other);
-
- base = new_base;
- new_base = NULL;
- other = NULL;
- }
-
-done:
- if (error == 0)
- *out = base;
- else
- git_annotated_commit_free(base);
-
- git_annotated_commit_free(other);
- git_annotated_commit_free(new_base);
- git_oidarray_free(&bases);
- git_array_clear(head_ids);
- return error;
-}
-
-static int iterator_for_annotated_commit(
- git_iterator **out,
- git_annotated_commit *commit)
-{
- git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
- int error;
-
- opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
-
- if (commit == NULL) {
- error = git_iterator_for_nothing(out, &opts);
- } else if (commit->type == GIT_ANNOTATED_COMMIT_VIRTUAL) {
- error = git_iterator_for_index(out, git_index_owner(commit->index), commit->index, &opts);
- } else {
- if (!commit->tree &&
- (error = git_commit_tree(&commit->tree, commit->commit)) < 0)
- goto done;
-
- error = git_iterator_for_tree(out, commit->tree, &opts);
- }
-
-done:
- return error;
-}
-
-static int merge_annotated_commits(
- git_index **index_out,
- git_annotated_commit **base_out,
- git_repository *repo,
- git_annotated_commit *ours,
- git_annotated_commit *theirs,
- size_t recursion_level,
- const git_merge_options *opts)
-{
- git_annotated_commit *base = NULL;
- git_iterator *base_iter = NULL, *our_iter = NULL, *their_iter = NULL;
- int error;
-
- if ((error = compute_base(&base, repo, ours, theirs, opts,
- recursion_level)) < 0) {
-
- if (error != GIT_ENOTFOUND)
- goto done;
-
- giterr_clear();
- }
-
- if ((error = iterator_for_annotated_commit(&base_iter, base)) < 0 ||
- (error = iterator_for_annotated_commit(&our_iter, ours)) < 0 ||
- (error = iterator_for_annotated_commit(&their_iter, theirs)) < 0 ||
- (error = git_merge__iterators(index_out, repo, base_iter, our_iter,
- their_iter, opts)) < 0)
- goto done;
-
- if (base_out) {
- *base_out = base;
- base = NULL;
- }
-
-done:
- git_annotated_commit_free(base);
- git_iterator_free(base_iter);
- git_iterator_free(our_iter);
- git_iterator_free(their_iter);
- return error;
-}
-
-
-int git_merge_commits(
- git_index **out,
- git_repository *repo,
- const git_commit *our_commit,
- const git_commit *their_commit,
- const git_merge_options *opts)
-{
- git_annotated_commit *ours = NULL, *theirs = NULL, *base = NULL;
- int error = 0;
-
- if ((error = git_annotated_commit_from_commit(&ours, (git_commit *)our_commit)) < 0 ||
- (error = git_annotated_commit_from_commit(&theirs, (git_commit *)their_commit)) < 0)
- goto done;
-
- error = merge_annotated_commits(out, &base, repo, ours, theirs, 0, opts);
-
-done:
- git_annotated_commit_free(ours);
- git_annotated_commit_free(theirs);
- git_annotated_commit_free(base);
- return error;
-}
-
-/* Merge setup / cleanup */
-
-static int write_merge_head(
- git_repository *repo,
- const git_annotated_commit *heads[],
- size_t heads_len)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf file_path = GIT_BUF_INIT;
- size_t i;
- int error = 0;
-
- assert(repo && heads);
-
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_HEAD_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
- goto cleanup;
-
- for (i = 0; i < heads_len; i++) {
- if ((error = git_filebuf_printf(&file, "%s\n", heads[i]->id_str)) < 0)
- goto cleanup;
- }
-
- error = git_filebuf_commit(&file);
-
-cleanup:
- if (error < 0)
- git_filebuf_cleanup(&file);
-
- git_buf_free(&file_path);
-
- return error;
-}
-
-static int write_merge_mode(git_repository *repo)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf file_path = GIT_BUF_INIT;
- int error = 0;
-
- assert(repo);
-
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MODE_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
- goto cleanup;
-
- if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0)
- goto cleanup;
-
- error = git_filebuf_commit(&file);
-
-cleanup:
- if (error < 0)
- git_filebuf_cleanup(&file);
-
- git_buf_free(&file_path);
-
- return error;
-}
-
-struct merge_msg_entry {
- const git_annotated_commit *merge_head;
- bool written;
-};
-
-static int msg_entry_is_branch(
- const struct merge_msg_entry *entry,
- git_vector *entries)
-{
- GIT_UNUSED(entries);
-
- return (entry->written == 0 &&
- entry->merge_head->remote_url == NULL &&
- entry->merge_head->ref_name != NULL &&
- git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0);
-}
-
-static int msg_entry_is_tracking(
- const struct merge_msg_entry *entry,
- git_vector *entries)
-{
- GIT_UNUSED(entries);
-
- return (entry->written == 0 &&
- entry->merge_head->remote_url == NULL &&
- entry->merge_head->ref_name != NULL &&
- git__strncmp(GIT_REFS_REMOTES_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_REMOTES_DIR)) == 0);
-}
-
-static int msg_entry_is_tag(
- const struct merge_msg_entry *entry,
- git_vector *entries)
-{
- GIT_UNUSED(entries);
-
- return (entry->written == 0 &&
- entry->merge_head->remote_url == NULL &&
- entry->merge_head->ref_name != NULL &&
- git__strncmp(GIT_REFS_TAGS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_TAGS_DIR)) == 0);
-}
-
-static int msg_entry_is_remote(
- const struct merge_msg_entry *entry,
- git_vector *entries)
-{
- if (entry->written == 0 &&
- entry->merge_head->remote_url != NULL &&
- entry->merge_head->ref_name != NULL &&
- git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0)
- {
- struct merge_msg_entry *existing;
-
- /* Match only branches from the same remote */
- if (entries->length == 0)
- return 1;
-
- existing = git_vector_get(entries, 0);
-
- return (git__strcmp(existing->merge_head->remote_url,
- entry->merge_head->remote_url) == 0);
- }
-
- return 0;
-}
-
-static int msg_entry_is_oid(
- const struct merge_msg_entry *merge_msg_entry)
-{
- return (merge_msg_entry->written == 0 &&
- merge_msg_entry->merge_head->ref_name == NULL &&
- merge_msg_entry->merge_head->remote_url == NULL);
-}
-
-static int merge_msg_entry_written(
- const struct merge_msg_entry *merge_msg_entry)
-{
- return (merge_msg_entry->written == 1);
-}
-
-static int merge_msg_entries(
- git_vector *v,
- const struct merge_msg_entry *entries,
- size_t len,
- int (*match)(const struct merge_msg_entry *entry, git_vector *entries))
-{
- size_t i;
- int matches, total = 0;
-
- git_vector_clear(v);
-
- for (i = 0; i < len; i++) {
- if ((matches = match(&entries[i], v)) < 0)
- return matches;
- else if (!matches)
- continue;
-
- git_vector_insert(v, (struct merge_msg_entry *)&entries[i]);
- total++;
- }
-
- return total;
-}
-
-static int merge_msg_write_entries(
- git_filebuf *file,
- git_vector *entries,
- const char *item_name,
- const char *item_plural_name,
- size_t ref_name_skip,
- const char *source,
- char sep)
-{
- struct merge_msg_entry *entry;
- size_t i;
- int error = 0;
-
- if (entries->length == 0)
- return 0;
-
- if (sep && (error = git_filebuf_printf(file, "%c ", sep)) < 0)
- goto done;
-
- if ((error = git_filebuf_printf(file, "%s ",
- (entries->length == 1) ? item_name : item_plural_name)) < 0)
- goto done;
-
- git_vector_foreach(entries, i, entry) {
- if (i > 0 &&
- (error = git_filebuf_printf(file, "%s", (i == entries->length - 1) ? " and " : ", ")) < 0)
- goto done;
-
- if ((error = git_filebuf_printf(file, "'%s'", entry->merge_head->ref_name + ref_name_skip)) < 0)
- goto done;
-
- entry->written = 1;
- }
-
- if (source)
- error = git_filebuf_printf(file, " of %s", source);
-
-done:
- return error;
-}
-
-static int merge_msg_write_branches(
- git_filebuf *file,
- git_vector *entries,
- char sep)
-{
- return merge_msg_write_entries(file, entries,
- "branch", "branches", strlen(GIT_REFS_HEADS_DIR), NULL, sep);
-}
-
-static int merge_msg_write_tracking(
- git_filebuf *file,
- git_vector *entries,
- char sep)
-{
- return merge_msg_write_entries(file, entries,
- "remote-tracking branch", "remote-tracking branches", 0, NULL, sep);
-}
-
-static int merge_msg_write_tags(
- git_filebuf *file,
- git_vector *entries,
- char sep)
-{
- return merge_msg_write_entries(file, entries,
- "tag", "tags", strlen(GIT_REFS_TAGS_DIR), NULL, sep);
-}
-
-static int merge_msg_write_remotes(
- git_filebuf *file,
- git_vector *entries,
- char sep)
-{
- const char *source;
-
- if (entries->length == 0)
- return 0;
-
- source = ((struct merge_msg_entry *)entries->contents[0])->merge_head->remote_url;
-
- return merge_msg_write_entries(file, entries,
- "branch", "branches", strlen(GIT_REFS_HEADS_DIR), source, sep);
-}
-
-static int write_merge_msg(
- git_repository *repo,
- const git_annotated_commit *heads[],
- size_t heads_len)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf file_path = GIT_BUF_INIT;
- struct merge_msg_entry *entries;
- git_vector matching = GIT_VECTOR_INIT;
- size_t i;
- char sep = 0;
- int error = 0;
-
- assert(repo && heads);
-
- entries = git__calloc(heads_len, sizeof(struct merge_msg_entry));
- GITERR_CHECK_ALLOC(entries);
-
- if (git_vector_init(&matching, heads_len, NULL) < 0) {
- git__free(entries);
- return -1;
- }
-
- for (i = 0; i < heads_len; i++)
- entries[i].merge_head = heads[i];
-
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0 ||
- (error = git_filebuf_write(&file, "Merge ", 6)) < 0)
- goto cleanup;
-
- /*
- * This is to emulate the format of MERGE_MSG by core git.
- *
- * Core git will write all the commits specified by OID, in the order
- * provided, until the first named branch or tag is reached, at which
- * point all branches will be written in the order provided, then all
- * tags, then all remote tracking branches and finally all commits that
- * were specified by OID that were not already written.
- *
- * Yes. Really.
- */
- for (i = 0; i < heads_len; i++) {
- if (!msg_entry_is_oid(&entries[i]))
- break;
-
- if ((error = git_filebuf_printf(&file,
- "%scommit '%s'", (i > 0) ? "; " : "",
- entries[i].merge_head->id_str)) < 0)
- goto cleanup;
-
- entries[i].written = 1;
- }
-
- if (i)
- sep = ';';
-
- if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_branch)) < 0 ||
- (error = merge_msg_write_branches(&file, &matching, sep)) < 0)
- goto cleanup;
-
- if (matching.length)
- sep =',';
-
- if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tracking)) < 0 ||
- (error = merge_msg_write_tracking(&file, &matching, sep)) < 0)
- goto cleanup;
-
- if (matching.length)
- sep =',';
-
- if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tag)) < 0 ||
- (error = merge_msg_write_tags(&file, &matching, sep)) < 0)
- goto cleanup;
-
- if (matching.length)
- sep =',';
-
- /* We should never be called with multiple remote branches, but handle
- * it in case we are... */
- while ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_remote)) > 0) {
- if ((error = merge_msg_write_remotes(&file, &matching, sep)) < 0)
- goto cleanup;
-
- if (matching.length)
- sep =',';
- }
-
- if (error < 0)
- goto cleanup;
-
- for (i = 0; i < heads_len; i++) {
- if (merge_msg_entry_written(&entries[i]))
- continue;
-
- if ((error = git_filebuf_printf(&file, "; commit '%s'",
- entries[i].merge_head->id_str)) < 0)
- goto cleanup;
- }
-
- if ((error = git_filebuf_printf(&file, "\n")) < 0 ||
- (error = git_filebuf_commit(&file)) < 0)
- goto cleanup;
-
-cleanup:
- if (error < 0)
- git_filebuf_cleanup(&file);
-
- git_buf_free(&file_path);
-
- git_vector_free(&matching);
- git__free(entries);
-
- return error;
-}
-
-int git_merge__setup(
- git_repository *repo,
- const git_annotated_commit *our_head,
- const git_annotated_commit *heads[],
- size_t heads_len)
-{
- int error = 0;
-
- assert (repo && our_head && heads);
-
- if ((error = git_repository__set_orig_head(repo, git_annotated_commit_id(our_head))) == 0 &&
- (error = write_merge_head(repo, heads, heads_len)) == 0 &&
- (error = write_merge_mode(repo)) == 0) {
- error = write_merge_msg(repo, heads, heads_len);
- }
-
- return error;
-}
-
-/* Merge branches */
-
-static int merge_ancestor_head(
- git_annotated_commit **ancestor_head,
- git_repository *repo,
- const git_annotated_commit *our_head,
- const git_annotated_commit **their_heads,
- size_t their_heads_len)
-{
- git_oid *oids, ancestor_oid;
- size_t i, alloc_len;
- int error = 0;
-
- assert(repo && our_head && their_heads);
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, their_heads_len, 1);
- oids = git__calloc(alloc_len, sizeof(git_oid));
- GITERR_CHECK_ALLOC(oids);
-
- git_oid_cpy(&oids[0], git_commit_id(our_head->commit));
-
- for (i = 0; i < their_heads_len; i++)
- git_oid_cpy(&oids[i + 1], git_annotated_commit_id(their_heads[i]));
-
- if ((error = git_merge_base_many(&ancestor_oid, repo, their_heads_len + 1, oids)) < 0)
- goto on_error;
-
- error = git_annotated_commit_lookup(ancestor_head, repo, &ancestor_oid);
-
-on_error:
- git__free(oids);
- return error;
-}
-
-const char *merge_their_label(const char *branchname)
-{
- const char *slash;
-
- if ((slash = strrchr(branchname, '/')) == NULL)
- return branchname;
-
- if (*(slash+1) == '\0')
- return "theirs";
-
- return slash+1;
-}
-
-static int merge_normalize_checkout_opts(
- git_checkout_options *out,
- git_repository *repo,
- const git_checkout_options *given_checkout_opts,
- unsigned int checkout_strategy,
- git_annotated_commit *ancestor,
- const git_annotated_commit *our_head,
- const git_annotated_commit **their_heads,
- size_t their_heads_len)
-{
- git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
- int error = 0;
-
- GIT_UNUSED(repo);
-
- if (given_checkout_opts != NULL)
- memcpy(out, given_checkout_opts, sizeof(git_checkout_options));
- else
- memcpy(out, &default_checkout_opts, sizeof(git_checkout_options));
-
- out->checkout_strategy = checkout_strategy;
-
- if (!out->ancestor_label) {
- if (ancestor && ancestor->type == GIT_ANNOTATED_COMMIT_REAL)
- out->ancestor_label = git_commit_summary(ancestor->commit);
- else if (ancestor)
- out->ancestor_label = "merged common ancestors";
- else
- out->ancestor_label = "empty base";
- }
-
- if (!out->our_label) {
- if (our_head && our_head->ref_name)
- out->our_label = our_head->ref_name;
- else
- out->our_label = "ours";
- }
-
- if (!out->their_label) {
- if (their_heads_len == 1 && their_heads[0]->ref_name)
- out->their_label = merge_their_label(their_heads[0]->ref_name);
- else if (their_heads_len == 1)
- out->their_label = their_heads[0]->id_str;
- else
- out->their_label = "theirs";
- }
-
- return error;
-}
-
-static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
-{
- git_tree *head_tree = NULL;
- git_index *index_repo = NULL;
- git_iterator *iter_repo = NULL, *iter_new = NULL;
- git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
- git_diff *staged_diff_list = NULL, *index_diff_list = NULL;
- git_diff_delta *delta;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_vector staged_paths = GIT_VECTOR_INIT;
- size_t i;
- int error = 0;
-
- GIT_UNUSED(merged_paths);
-
- *conflicts = 0;
-
- /* No staged changes may exist unless the change staged is identical to
- * the result of the merge. This allows one to apply to merge manually,
- * then run merge. Any other staged change would be overwritten by
- * a reset merge.
- */
- if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
- (error = git_repository_index(&index_repo, repo)) < 0 ||
- (error = git_diff_tree_to_index(&staged_diff_list, repo, head_tree, index_repo, &opts)) < 0)
- goto done;
-
- if (staged_diff_list->deltas.length == 0)
- goto done;
-
- git_vector_foreach(&staged_diff_list->deltas, i, delta) {
- if ((error = git_vector_insert(&staged_paths, (char *)delta->new_file.path)) < 0)
- goto done;
- }
-
- iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
- iter_opts.pathlist.strings = (char **)staged_paths.contents;
- iter_opts.pathlist.count = staged_paths.length;
-
- if ((error = git_iterator_for_index(&iter_repo, repo, index_repo, &iter_opts)) < 0 ||
- (error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 ||
- (error = git_diff__from_iterators(&index_diff_list, repo, iter_repo, iter_new, &opts)) < 0)
- goto done;
-
- *conflicts = index_diff_list->deltas.length;
-
-done:
- git_tree_free(head_tree);
- git_index_free(index_repo);
- git_iterator_free(iter_repo);
- git_iterator_free(iter_new);
- git_diff_free(staged_diff_list);
- git_diff_free(index_diff_list);
- git_vector_free(&staged_paths);
-
- return error;
-}
-
-static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
-{
- git_diff *wd_diff_list = NULL;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- int error = 0;
-
- GIT_UNUSED(index_new);
-
- *conflicts = 0;
-
- /* We need to have merged at least 1 file for the possibility to exist to
- * have conflicts with the workdir. Passing 0 as the pathspec count paramter
- * will consider all files in the working directory, that is, we may detect
- * a conflict if there were untracked files in the workdir prior to starting
- * the merge. This typically happens when cherry-picking a commmit whose
- * changes have already been applied.
- */
- if (merged_paths->length == 0)
- return 0;
-
- opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
-
- /* Workdir changes may exist iff they do not conflict with changes that
- * will be applied by the merge (including conflicts). Ensure that there
- * are no changes in the workdir to these paths.
- */
- opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
- opts.pathspec.count = merged_paths->length;
- opts.pathspec.strings = (char **)merged_paths->contents;
- opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL;
-
- if ((error = git_diff_index_to_workdir(&wd_diff_list, repo, NULL, &opts)) < 0)
- goto done;
-
- *conflicts = wd_diff_list->deltas.length;
-
-done:
- git_diff_free(wd_diff_list);
-
- return error;
-}
-
-int git_merge__check_result(git_repository *repo, git_index *index_new)
-{
- git_tree *head_tree = NULL;
- git_iterator *iter_head = NULL, *iter_new = NULL;
- git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
- git_diff *merged_list = NULL;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_delta *delta;
- git_vector paths = GIT_VECTOR_INIT;
- size_t i, index_conflicts = 0, wd_conflicts = 0, conflicts;
- const git_index_entry *e;
- int error = 0;
-
- iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
-
- if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
- (error = git_iterator_for_tree(&iter_head, head_tree, &iter_opts)) < 0 ||
- (error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 ||
- (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0)
- goto done;
-
- git_vector_foreach(&merged_list->deltas, i, delta) {
- if ((error = git_vector_insert(&paths, (char *)delta->new_file.path)) < 0)
- goto done;
- }
-
- for (i = 0; i < git_index_entrycount(index_new); i++) {
- e = git_index_get_byindex(index_new, i);
-
- if (git_index_entry_is_conflict(e) &&
- (git_vector_last(&paths) == NULL ||
- strcmp(git_vector_last(&paths), e->path) != 0)) {
-
- if ((error = git_vector_insert(&paths, (char *)e->path)) < 0)
- goto done;
- }
- }
-
- /* Make sure the index and workdir state do not prevent merging */
- if ((error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 ||
- (error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0)
- goto done;
-
- if ((conflicts = index_conflicts + wd_conflicts) > 0) {
- giterr_set(GITERR_MERGE, "%" PRIuZ " uncommitted change%s would be overwritten by merge",
- conflicts, (conflicts != 1) ? "s" : "");
- error = GIT_ECONFLICT;
- }
-
-done:
- git_vector_free(&paths);
- git_tree_free(head_tree);
- git_iterator_free(iter_head);
- git_iterator_free(iter_new);
- git_diff_free(merged_list);
-
- return error;
-}
-
-int git_merge__append_conflicts_to_merge_msg(
- git_repository *repo,
- git_index *index)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf file_path = GIT_BUF_INIT;
- const char *last = NULL;
- size_t i;
- int error;
-
- if (!git_index_has_conflicts(index))
- return 0;
-
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0)
- goto cleanup;
-
- git_filebuf_printf(&file, "\nConflicts:\n");
-
- for (i = 0; i < git_index_entrycount(index); i++) {
- const git_index_entry *e = git_index_get_byindex(index, i);
-
- if (!git_index_entry_is_conflict(e))
- continue;
-
- if (last == NULL || strcmp(e->path, last) != 0)
- git_filebuf_printf(&file, "\t%s\n", e->path);
-
- last = e->path;
- }
-
- error = git_filebuf_commit(&file);
-
-cleanup:
- if (error < 0)
- git_filebuf_cleanup(&file);
-
- git_buf_free(&file_path);
-
- return error;
-}
-
-static int merge_state_cleanup(git_repository *repo)
-{
- const char *state_files[] = {
- GIT_MERGE_HEAD_FILE,
- GIT_MERGE_MODE_FILE,
- GIT_MERGE_MSG_FILE,
- };
-
- return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
-}
-
-static int merge_heads(
- git_annotated_commit **ancestor_head_out,
- git_annotated_commit **our_head_out,
- git_repository *repo,
- const git_annotated_commit **their_heads,
- size_t their_heads_len)
-{
- git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
- git_reference *our_ref = NULL;
- int error = 0;
-
- *ancestor_head_out = NULL;
- *our_head_out = NULL;
-
- if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
- goto done;
-
- if ((error = git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE)) < 0 ||
- (error = git_annotated_commit_from_ref(&our_head, repo, our_ref)) < 0)
- goto done;
-
- if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0) {
- if (error != GIT_ENOTFOUND)
- goto done;
-
- giterr_clear();
- error = 0;
- }
-
- *ancestor_head_out = ancestor_head;
- *our_head_out = our_head;
-
-done:
- if (error < 0) {
- git_annotated_commit_free(ancestor_head);
- git_annotated_commit_free(our_head);
- }
-
- git_reference_free(our_ref);
-
- return error;
-}
-
-static int merge_preference(git_merge_preference_t *out, git_repository *repo)
-{
- git_config *config;
- const char *value;
- int bool_value, error = 0;
-
- *out = GIT_MERGE_PREFERENCE_NONE;
-
- if ((error = git_repository_config_snapshot(&config, repo)) < 0)
- goto done;
-
- if ((error = git_config_get_string(&value, config, "merge.ff")) < 0) {
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
-
- goto done;
- }
-
- if (git_config_parse_bool(&bool_value, value) == 0) {
- if (!bool_value)
- *out |= GIT_MERGE_PREFERENCE_NO_FASTFORWARD;
- } else {
- if (strcasecmp(value, "only") == 0)
- *out |= GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY;
- }
-
-done:
- git_config_free(config);
- return error;
-}
-
-int git_merge_analysis(
- git_merge_analysis_t *analysis_out,
- git_merge_preference_t *preference_out,
- git_repository *repo,
- const git_annotated_commit **their_heads,
- size_t their_heads_len)
-{
- git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
- int error = 0;
-
- assert(analysis_out && preference_out && repo && their_heads);
-
- if (their_heads_len != 1) {
- giterr_set(GITERR_MERGE, "Can only merge a single branch");
- error = -1;
- goto done;
- }
-
- *analysis_out = GIT_MERGE_ANALYSIS_NONE;
-
- if ((error = merge_preference(preference_out, repo)) < 0)
- goto done;
-
- if (git_repository_head_unborn(repo)) {
- *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
- goto done;
- }
-
- if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0)
- goto done;
-
- /* We're up-to-date if we're trying to merge our own common ancestor. */
- if (ancestor_head && git_oid_equal(
- git_annotated_commit_id(ancestor_head), git_annotated_commit_id(their_heads[0])))
- *analysis_out |= GIT_MERGE_ANALYSIS_UP_TO_DATE;
-
- /* We're fastforwardable if we're our own common ancestor. */
- else if (ancestor_head && git_oid_equal(
- git_annotated_commit_id(ancestor_head), git_annotated_commit_id(our_head)))
- *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL;
-
- /* Otherwise, just a normal merge is possible. */
- else
- *analysis_out |= GIT_MERGE_ANALYSIS_NORMAL;
-
-done:
- git_annotated_commit_free(ancestor_head);
- git_annotated_commit_free(our_head);
- return error;
-}
-
-int git_merge(
- git_repository *repo,
- const git_annotated_commit **their_heads,
- size_t their_heads_len,
- const git_merge_options *merge_opts,
- const git_checkout_options *given_checkout_opts)
-{
- git_reference *our_ref = NULL;
- git_checkout_options checkout_opts;
- git_annotated_commit *our_head = NULL, *base = NULL;
- git_index *index = NULL;
- git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
- unsigned int checkout_strategy;
- int error = 0;
-
- assert(repo && their_heads);
-
- if (their_heads_len != 1) {
- giterr_set(GITERR_MERGE, "Can only merge a single branch");
- return -1;
- }
-
- if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
- goto done;
-
- checkout_strategy = given_checkout_opts ?
- given_checkout_opts->checkout_strategy :
- GIT_CHECKOUT_SAFE;
-
- if ((error = git_indexwriter_init_for_operation(&indexwriter, repo,
- &checkout_strategy)) < 0)
- goto done;
-
- /* Write the merge setup files to the repository. */
- if ((error = git_annotated_commit_from_head(&our_head, repo)) < 0 ||
- (error = git_merge__setup(repo, our_head, their_heads,
- their_heads_len)) < 0)
- goto done;
-
- /* TODO: octopus */
-
- if ((error = merge_annotated_commits(&index, &base, repo, our_head,
- (git_annotated_commit *)their_heads[0], 0, merge_opts)) < 0 ||
- (error = git_merge__check_result(repo, index)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0)
- goto done;
-
- /* check out the merge results */
-
- if ((error = merge_normalize_checkout_opts(&checkout_opts, repo,
- given_checkout_opts, checkout_strategy,
- base, our_head, their_heads, their_heads_len)) < 0 ||
- (error = git_checkout_index(repo, index, &checkout_opts)) < 0)
- goto done;
-
- error = git_indexwriter_commit(&indexwriter);
-
-done:
- if (error < 0)
- merge_state_cleanup(repo);
-
- git_indexwriter_cleanup(&indexwriter);
- git_index_free(index);
- git_annotated_commit_free(our_head);
- git_annotated_commit_free(base);
- git_reference_free(our_ref);
-
- return error;
-}
-
-int git_merge_init_options(git_merge_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_merge_options, GIT_MERGE_OPTIONS_INIT);
- return 0;
-}
-
-int git_merge_file_init_input(git_merge_file_input *input, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- input, version, git_merge_file_input, GIT_MERGE_FILE_INPUT_INIT);
- return 0;
-}
-
-int git_merge_file_init_options(
- git_merge_file_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_merge_file_options, GIT_MERGE_FILE_OPTIONS_INIT);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_merge_h__
-#define INCLUDE_merge_h__
-
-#include "vector.h"
-#include "commit_list.h"
-#include "pool.h"
-#include "iterator.h"
-
-#include "git2/types.h"
-#include "git2/merge.h"
-#include "git2/sys/merge.h"
-
-#define GIT_MERGE_MSG_FILE "MERGE_MSG"
-#define GIT_MERGE_MODE_FILE "MERGE_MODE"
-#define GIT_MERGE_FILE_MODE 0666
-
-#define GIT_MERGE_DEFAULT_RENAME_THRESHOLD 50
-#define GIT_MERGE_DEFAULT_TARGET_LIMIT 1000
-
-
-/** Internal merge flags. */
-enum {
- /** The merge is for a virtual base in a recursive merge. */
- GIT_MERGE__VIRTUAL_BASE = (1 << 31),
-};
-
-enum {
- /** Accept the conflict file, staging it as the merge result. */
- GIT_MERGE_FILE_FAVOR__CONFLICTED = 4,
-};
-
-
-/** Types of changes when files are merged from branch to branch. */
-typedef enum {
- /* No conflict - a change only occurs in one branch. */
- GIT_MERGE_DIFF_NONE = 0,
-
- /* Occurs when a file is modified in both branches. */
- GIT_MERGE_DIFF_BOTH_MODIFIED = (1 << 0),
-
- /* Occurs when a file is added in both branches. */
- GIT_MERGE_DIFF_BOTH_ADDED = (1 << 1),
-
- /* Occurs when a file is deleted in both branches. */
- GIT_MERGE_DIFF_BOTH_DELETED = (1 << 2),
-
- /* Occurs when a file is modified in one branch and deleted in the other. */
- GIT_MERGE_DIFF_MODIFIED_DELETED = (1 << 3),
-
- /* Occurs when a file is renamed in one branch and modified in the other. */
- GIT_MERGE_DIFF_RENAMED_MODIFIED = (1 << 4),
-
- /* Occurs when a file is renamed in one branch and deleted in the other. */
- GIT_MERGE_DIFF_RENAMED_DELETED = (1 << 5),
-
- /* Occurs when a file is renamed in one branch and a file with the same
- * name is added in the other. Eg, A->B and new file B. Core git calls
- * this a "rename/delete". */
- GIT_MERGE_DIFF_RENAMED_ADDED = (1 << 6),
-
- /* Occurs when both a file is renamed to the same name in the ours and
- * theirs branches. Eg, A->B and A->B in both. Automergeable. */
- GIT_MERGE_DIFF_BOTH_RENAMED = (1 << 7),
-
- /* Occurs when a file is renamed to different names in the ours and theirs
- * branches. Eg, A->B and A->C. */
- GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 = (1 << 8),
-
- /* Occurs when two files are renamed to the same name in the ours and
- * theirs branches. Eg, A->C and B->C. */
- GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 = (1 << 9),
-
- /* Occurs when an item at a path in one branch is a directory, and an
- * item at the same path in a different branch is a file. */
- GIT_MERGE_DIFF_DIRECTORY_FILE = (1 << 10),
-
- /* The child of a folder that is in a directory/file conflict. */
- GIT_MERGE_DIFF_DF_CHILD = (1 << 11),
-} git_merge_diff_type_t;
-
-typedef struct {
- git_repository *repo;
- git_pool pool;
-
- /* Vector of git_index_entry that represent the merged items that
- * have been staged, either because only one side changed, or because
- * the two changes were non-conflicting and mergeable. These items
- * will be written as staged entries in the main index.
- */
- git_vector staged;
-
- /* Vector of git_merge_diff entries that represent the conflicts that
- * have not been automerged. These items will be written to high-stage
- * entries in the main index.
- */
- git_vector conflicts;
-
- /* Vector of git_merge_diff that have been automerged. These items
- * will be written to the REUC when the index is produced.
- */
- git_vector resolved;
-} git_merge_diff_list;
-
-/**
- * Description of changes to one file across three trees.
- */
-typedef struct {
- git_merge_diff_type_t type;
-
- git_index_entry ancestor_entry;
-
- git_index_entry our_entry;
- git_delta_t our_status;
-
- git_index_entry their_entry;
- git_delta_t their_status;
-
-} git_merge_diff;
-
-int git_merge__bases_many(
- git_commit_list **out,
- git_revwalk *walk,
- git_commit_list_node *one,
- git_vector *twos);
-
-/*
- * Three-way tree differencing
- */
-
-git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo);
-
-int git_merge_diff_list__find_differences(
- git_merge_diff_list *merge_diff_list,
- git_iterator *ancestor_iterator,
- git_iterator *ours_iter,
- git_iterator *theirs_iter);
-
-int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_options *opts);
-
-void git_merge_diff_list__free(git_merge_diff_list *diff_list);
-
-/* Merge metadata setup */
-
-int git_merge__setup(
- git_repository *repo,
- const git_annotated_commit *our_head,
- const git_annotated_commit *heads[],
- size_t heads_len);
-
-int git_merge__iterators(
- git_index **out,
- git_repository *repo,
- git_iterator *ancestor_iter,
- git_iterator *our_iter,
- git_iterator *their_iter,
- const git_merge_options *given_opts);
-
-int git_merge__check_result(git_repository *repo, git_index *index_new);
-
-int git_merge__append_conflicts_to_merge_msg(git_repository *repo, git_index *index);
-
-/* Merge files */
-
-GIT_INLINE(const char *) git_merge_file__best_path(
- const char *ancestor,
- const char *ours,
- const char *theirs)
-{
- if (!ancestor) {
- if (ours && theirs && strcmp(ours, theirs) == 0)
- return ours;
-
- return NULL;
- }
-
- if (ours && strcmp(ancestor, ours) == 0)
- return theirs;
- else if(theirs && strcmp(ancestor, theirs) == 0)
- return ours;
-
- return NULL;
-}
-
-GIT_INLINE(uint32_t) git_merge_file__best_mode(
- uint32_t ancestor, uint32_t ours, uint32_t theirs)
-{
- /*
- * If ancestor didn't exist and either ours or theirs is executable,
- * assume executable. Otherwise, if any mode changed from the ancestor,
- * use that one.
- */
- if (!ancestor) {
- if (ours == GIT_FILEMODE_BLOB_EXECUTABLE ||
- theirs == GIT_FILEMODE_BLOB_EXECUTABLE)
- return GIT_FILEMODE_BLOB_EXECUTABLE;
-
- return GIT_FILEMODE_BLOB;
- } else if (ours && theirs) {
- if (ancestor == ours)
- return theirs;
-
- return ours;
- }
-
- return 0;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "vector.h"
-#include "global.h"
-#include "merge.h"
-#include "merge_driver.h"
-#include "git2/merge.h"
-#include "git2/sys/merge.h"
-
-static const char *merge_driver_name__text = "text";
-static const char *merge_driver_name__union = "union";
-static const char *merge_driver_name__binary = "binary";
-
-struct merge_driver_registry {
- git_rwlock lock;
- git_vector drivers;
-};
-
-typedef struct {
- git_merge_driver *driver;
- int initialized;
- char name[GIT_FLEX_ARRAY];
-} git_merge_driver_entry;
-
-static struct merge_driver_registry merge_driver_registry;
-
-static void git_merge_driver_global_shutdown(void);
-
-
-int git_merge_driver__builtin_apply(
- git_merge_driver *self,
- const char **path_out,
- uint32_t *mode_out,
- git_buf *merged_out,
- const char *filter_name,
- const git_merge_driver_source *src)
-{
- git_merge_driver__builtin *driver = (git_merge_driver__builtin *)self;
- git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
- git_merge_file_result result = {0};
- int error;
-
- GIT_UNUSED(filter_name);
-
- if (src->file_opts)
- memcpy(&file_opts, src->file_opts, sizeof(git_merge_file_options));
-
- if (driver->favor)
- file_opts.favor = driver->favor;
-
- if ((error = git_merge_file_from_index(&result, src->repo,
- src->ancestor, src->ours, src->theirs, &file_opts)) < 0)
- goto done;
-
- if (!result.automergeable &&
- !(file_opts.flags & GIT_MERGE_FILE_FAVOR__CONFLICTED)) {
- error = GIT_EMERGECONFLICT;
- goto done;
- }
-
- *path_out = git_merge_file__best_path(
- src->ancestor ? src->ancestor->path : NULL,
- src->ours ? src->ours->path : NULL,
- src->theirs ? src->theirs->path : NULL);
-
- *mode_out = git_merge_file__best_mode(
- src->ancestor ? src->ancestor->mode : 0,
- src->ours ? src->ours->mode : 0,
- src->theirs ? src->theirs->mode : 0);
-
- merged_out->ptr = (char *)result.ptr;
- merged_out->size = result.len;
- merged_out->asize = result.len;
- result.ptr = NULL;
-
-done:
- git_merge_file_result_free(&result);
- return error;
-}
-
-static int merge_driver_binary_apply(
- git_merge_driver *self,
- const char **path_out,
- uint32_t *mode_out,
- git_buf *merged_out,
- const char *filter_name,
- const git_merge_driver_source *src)
-{
- GIT_UNUSED(self);
- GIT_UNUSED(path_out);
- GIT_UNUSED(mode_out);
- GIT_UNUSED(merged_out);
- GIT_UNUSED(filter_name);
- GIT_UNUSED(src);
-
- return GIT_EMERGECONFLICT;
-}
-
-static int merge_driver_entry_cmp(const void *a, const void *b)
-{
- const git_merge_driver_entry *entry_a = a;
- const git_merge_driver_entry *entry_b = b;
-
- return strcmp(entry_a->name, entry_b->name);
-}
-
-static int merge_driver_entry_search(const void *a, const void *b)
-{
- const char *name_a = a;
- const git_merge_driver_entry *entry_b = b;
-
- return strcmp(name_a, entry_b->name);
-}
-
-git_merge_driver__builtin git_merge_driver__text = {
- {
- GIT_MERGE_DRIVER_VERSION,
- NULL,
- NULL,
- git_merge_driver__builtin_apply,
- },
- GIT_MERGE_FILE_FAVOR_NORMAL
-};
-
-git_merge_driver__builtin git_merge_driver__union = {
- {
- GIT_MERGE_DRIVER_VERSION,
- NULL,
- NULL,
- git_merge_driver__builtin_apply,
- },
- GIT_MERGE_FILE_FAVOR_UNION
-};
-
-git_merge_driver git_merge_driver__binary = {
- GIT_MERGE_DRIVER_VERSION,
- NULL,
- NULL,
- merge_driver_binary_apply
-};
-
-/* Note: callers must lock the registry before calling this function */
-static int merge_driver_registry_insert(
- const char *name, git_merge_driver *driver)
-{
- git_merge_driver_entry *entry;
-
- entry = git__calloc(1, sizeof(git_merge_driver_entry) + strlen(name) + 1);
- GITERR_CHECK_ALLOC(entry);
-
- strcpy(entry->name, name);
- entry->driver = driver;
-
- return git_vector_insert_sorted(
- &merge_driver_registry.drivers, entry, NULL);
-}
-
-int git_merge_driver_global_init(void)
-{
- int error;
-
- if (git_rwlock_init(&merge_driver_registry.lock) < 0)
- return -1;
-
- if ((error = git_vector_init(&merge_driver_registry.drivers, 3,
- merge_driver_entry_cmp)) < 0)
- goto done;
-
- if ((error = merge_driver_registry_insert(
- merge_driver_name__text, &git_merge_driver__text.base)) < 0 ||
- (error = merge_driver_registry_insert(
- merge_driver_name__union, &git_merge_driver__union.base)) < 0 ||
- (error = merge_driver_registry_insert(
- merge_driver_name__binary, &git_merge_driver__binary)) < 0)
- goto done;
-
- git__on_shutdown(git_merge_driver_global_shutdown);
-
-done:
- if (error < 0)
- git_vector_free_deep(&merge_driver_registry.drivers);
-
- return error;
-}
-
-static void git_merge_driver_global_shutdown(void)
-{
- git_merge_driver_entry *entry;
- size_t i;
-
- if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0)
- return;
-
- git_vector_foreach(&merge_driver_registry.drivers, i, entry) {
- if (entry->driver->shutdown)
- entry->driver->shutdown(entry->driver);
-
- git__free(entry);
- }
-
- git_vector_free(&merge_driver_registry.drivers);
-
- git_rwlock_wrunlock(&merge_driver_registry.lock);
- git_rwlock_free(&merge_driver_registry.lock);
-}
-
-/* Note: callers must lock the registry before calling this function */
-static int merge_driver_registry_find(size_t *pos, const char *name)
-{
- return git_vector_search2(pos, &merge_driver_registry.drivers,
- merge_driver_entry_search, name);
-}
-
-/* Note: callers must lock the registry before calling this function */
-static git_merge_driver_entry *merge_driver_registry_lookup(
- size_t *pos, const char *name)
-{
- git_merge_driver_entry *entry = NULL;
-
- if (!merge_driver_registry_find(pos, name))
- entry = git_vector_get(&merge_driver_registry.drivers, *pos);
-
- return entry;
-}
-
-int git_merge_driver_register(const char *name, git_merge_driver *driver)
-{
- int error;
-
- assert(name && driver);
-
- if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
- giterr_set(GITERR_OS, "failed to lock merge driver registry");
- return -1;
- }
-
- if (!merge_driver_registry_find(NULL, name)) {
- giterr_set(GITERR_MERGE, "attempt to reregister existing driver '%s'",
- name);
- error = GIT_EEXISTS;
- goto done;
- }
-
- error = merge_driver_registry_insert(name, driver);
-
-done:
- git_rwlock_wrunlock(&merge_driver_registry.lock);
- return error;
-}
-
-int git_merge_driver_unregister(const char *name)
-{
- git_merge_driver_entry *entry;
- size_t pos;
- int error = 0;
-
- if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
- giterr_set(GITERR_OS, "failed to lock merge driver registry");
- return -1;
- }
-
- if ((entry = merge_driver_registry_lookup(&pos, name)) == NULL) {
- giterr_set(GITERR_MERGE, "cannot find merge driver '%s' to unregister",
- name);
- error = GIT_ENOTFOUND;
- goto done;
- }
-
- git_vector_remove(&merge_driver_registry.drivers, pos);
-
- if (entry->initialized && entry->driver->shutdown) {
- entry->driver->shutdown(entry->driver);
- entry->initialized = false;
- }
-
- git__free(entry);
-
-done:
- git_rwlock_wrunlock(&merge_driver_registry.lock);
- return error;
-}
-
-git_merge_driver *git_merge_driver_lookup(const char *name)
-{
- git_merge_driver_entry *entry;
- size_t pos;
- int error;
-
- /* If we've decided the merge driver to use internally - and not
- * based on user configuration (in merge_driver_name_for_path)
- * then we can use a hardcoded name to compare instead of bothering
- * to take a lock and look it up in the vector.
- */
- if (name == merge_driver_name__text)
- return &git_merge_driver__text.base;
- else if (name == merge_driver_name__binary)
- return &git_merge_driver__binary;
-
- if (git_rwlock_rdlock(&merge_driver_registry.lock) < 0) {
- giterr_set(GITERR_OS, "failed to lock merge driver registry");
- return NULL;
- }
-
- entry = merge_driver_registry_lookup(&pos, name);
-
- git_rwlock_rdunlock(&merge_driver_registry.lock);
-
- if (entry == NULL) {
- giterr_set(GITERR_MERGE, "cannot use an unregistered filter");
- return NULL;
- }
-
- if (!entry->initialized) {
- if (entry->driver->initialize &&
- (error = entry->driver->initialize(entry->driver)) < 0)
- return NULL;
-
- entry->initialized = 1;
- }
-
- return entry->driver;
-}
-
-static int merge_driver_name_for_path(
- const char **out,
- git_repository *repo,
- const char *path,
- const char *default_driver)
-{
- const char *value;
- int error;
-
- *out = NULL;
-
- if ((error = git_attr_get(&value, repo, 0, path, "merge")) < 0)
- return error;
-
- /* set: use the built-in 3-way merge driver ("text") */
- if (GIT_ATTR_TRUE(value))
- *out = merge_driver_name__text;
-
- /* unset: do not merge ("binary") */
- else if (GIT_ATTR_FALSE(value))
- *out = merge_driver_name__binary;
-
- else if (GIT_ATTR_UNSPECIFIED(value) && default_driver)
- *out = default_driver;
-
- else if (GIT_ATTR_UNSPECIFIED(value))
- *out = merge_driver_name__text;
-
- else
- *out = value;
-
- return 0;
-}
-
-
-GIT_INLINE(git_merge_driver *) merge_driver_lookup_with_wildcard(
- const char *name)
-{
- git_merge_driver *driver = git_merge_driver_lookup(name);
-
- if (driver == NULL)
- driver = git_merge_driver_lookup("*");
-
- return driver;
-}
-
-int git_merge_driver_for_source(
- const char **name_out,
- git_merge_driver **driver_out,
- const git_merge_driver_source *src)
-{
- const char *path, *driver_name;
- int error = 0;
-
- path = git_merge_file__best_path(
- src->ancestor ? src->ancestor->path : NULL,
- src->ours ? src->ours->path : NULL,
- src->theirs ? src->theirs->path : NULL);
-
- if ((error = merge_driver_name_for_path(
- &driver_name, src->repo, path, src->default_driver)) < 0)
- return error;
-
- *name_out = driver_name;
- *driver_out = merge_driver_lookup_with_wildcard(driver_name);
- return error;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_merge_driver_h__
-#define INCLUDE_merge_driver_h__
-
-#include "git2/merge.h"
-#include "git2/index.h"
-#include "git2/sys/merge.h"
-
-struct git_merge_driver_source {
- git_repository *repo;
- const char *default_driver;
- const git_merge_file_options *file_opts;
-
- const git_index_entry *ancestor;
- const git_index_entry *ours;
- const git_index_entry *theirs;
-};
-
-typedef struct git_merge_driver__builtin {
- git_merge_driver base;
- git_merge_file_favor_t favor;
-} git_merge_driver__builtin;
-
-extern int git_merge_driver_global_init(void);
-
-extern int git_merge_driver_for_path(
- char **name_out,
- git_merge_driver **driver_out,
- git_repository *repo,
- const char *path);
-
-/* Merge driver configuration */
-extern int git_merge_driver_for_source(
- const char **name_out,
- git_merge_driver **driver_out,
- const git_merge_driver_source *src);
-
-extern int git_merge_driver__builtin_apply(
- git_merge_driver *self,
- const char **path_out,
- uint32_t *mode_out,
- git_buf *merged_out,
- const char *filter_name,
- const git_merge_driver_source *src);
-
-/* Merge driver for text files, performs a standard three-way merge */
-extern git_merge_driver__builtin git_merge_driver__text;
-
-/* Merge driver for union-style merging */
-extern git_merge_driver__builtin git_merge_driver__union;
-
-/* Merge driver for unmergeable (binary) files: always produces conflicts */
-extern git_merge_driver git_merge_driver__binary;
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "repository.h"
-#include "posix.h"
-#include "fileops.h"
-#include "index.h"
-#include "diff_xdiff.h"
-#include "merge.h"
-
-#include "git2/repository.h"
-#include "git2/object.h"
-#include "git2/index.h"
-#include "git2/merge.h"
-
-#include "xdiff/xdiff.h"
-
-/* only examine the first 8000 bytes for binaryness.
- * https://github.com/git/git/blob/77bd3ea9f54f1584147b594abc04c26ca516d987/xdiff-interface.c#L197
- */
-#define GIT_MERGE_FILE_BINARY_SIZE 8000
-
-#define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
-
-int git_merge_file__input_from_index(
- git_merge_file_input *input_out,
- git_odb_object **odb_object_out,
- git_odb *odb,
- const git_index_entry *entry)
-{
- int error = 0;
-
- assert(input_out && odb_object_out && odb && entry);
-
- if ((error = git_odb_read(odb_object_out, odb, &entry->id)) < 0)
- goto done;
-
- input_out->path = entry->path;
- input_out->mode = entry->mode;
- input_out->ptr = (char *)git_odb_object_data(*odb_object_out);
- input_out->size = git_odb_object_size(*odb_object_out);
-
-done:
- return error;
-}
-
-static void merge_file_normalize_opts(
- git_merge_file_options *out,
- const git_merge_file_options *given_opts)
-{
- if (given_opts)
- memcpy(out, given_opts, sizeof(git_merge_file_options));
- else {
- git_merge_file_options default_opts = GIT_MERGE_FILE_OPTIONS_INIT;
- memcpy(out, &default_opts, sizeof(git_merge_file_options));
- }
-}
-
-static int merge_file__xdiff(
- git_merge_file_result *out,
- const git_merge_file_input *ancestor,
- const git_merge_file_input *ours,
- const git_merge_file_input *theirs,
- const git_merge_file_options *given_opts)
-{
- xmparam_t xmparam;
- mmfile_t ancestor_mmfile = {0}, our_mmfile = {0}, their_mmfile = {0};
- mmbuffer_t mmbuffer;
- git_merge_file_options options = GIT_MERGE_FILE_OPTIONS_INIT;
- const char *path;
- int xdl_result;
- int error = 0;
-
- memset(out, 0x0, sizeof(git_merge_file_result));
-
- merge_file_normalize_opts(&options, given_opts);
-
- memset(&xmparam, 0x0, sizeof(xmparam_t));
-
- if (ancestor) {
- xmparam.ancestor = (options.ancestor_label) ?
- options.ancestor_label : ancestor->path;
- ancestor_mmfile.ptr = (char *)ancestor->ptr;
- ancestor_mmfile.size = ancestor->size;
- }
-
- xmparam.file1 = (options.our_label) ?
- options.our_label : ours->path;
- our_mmfile.ptr = (char *)ours->ptr;
- our_mmfile.size = ours->size;
-
- xmparam.file2 = (options.their_label) ?
- options.their_label : theirs->path;
- their_mmfile.ptr = (char *)theirs->ptr;
- their_mmfile.size = theirs->size;
-
- if (options.favor == GIT_MERGE_FILE_FAVOR_OURS)
- xmparam.favor = XDL_MERGE_FAVOR_OURS;
- else if (options.favor == GIT_MERGE_FILE_FAVOR_THEIRS)
- xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
- else if (options.favor == GIT_MERGE_FILE_FAVOR_UNION)
- xmparam.favor = XDL_MERGE_FAVOR_UNION;
-
- xmparam.level = (options.flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM) ?
- XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS;
-
- if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3)
- xmparam.style = XDL_MERGE_DIFF3;
-
- if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE)
- xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE;
- if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE)
- xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
- if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL)
- xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
-
- if (options.flags & GIT_MERGE_FILE_DIFF_PATIENCE)
- xmparam.xpp.flags |= XDF_PATIENCE_DIFF;
-
- if (options.flags & GIT_MERGE_FILE_DIFF_MINIMAL)
- xmparam.xpp.flags |= XDF_NEED_MINIMAL;
-
- if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile,
- &their_mmfile, &xmparam, &mmbuffer)) < 0) {
- giterr_set(GITERR_MERGE, "Failed to merge files.");
- error = -1;
- goto done;
- }
-
- path = git_merge_file__best_path(
- ancestor ? ancestor->path : NULL,
- ours->path,
- theirs->path);
-
- if (path != NULL && (out->path = git__strdup(path)) == NULL) {
- error = -1;
- goto done;
- }
-
- out->automergeable = (xdl_result == 0);
- out->ptr = (const char *)mmbuffer.ptr;
- out->len = mmbuffer.size;
- out->mode = git_merge_file__best_mode(
- ancestor ? ancestor->mode : 0,
- ours->mode,
- theirs->mode);
-
-done:
- if (error < 0)
- git_merge_file_result_free(out);
-
- return error;
-}
-
-static bool merge_file__is_binary(const git_merge_file_input *file)
-{
- size_t len = file ? file->size : 0;
-
- if (len > GIT_XDIFF_MAX_SIZE)
- return true;
- if (len > GIT_MERGE_FILE_BINARY_SIZE)
- len = GIT_MERGE_FILE_BINARY_SIZE;
-
- return len ? (memchr(file->ptr, 0, len) != NULL) : false;
-}
-
-static int merge_file__binary(
- git_merge_file_result *out,
- const git_merge_file_input *ours,
- const git_merge_file_input *theirs,
- const git_merge_file_options *given_opts)
-{
- const git_merge_file_input *favored = NULL;
-
- memset(out, 0x0, sizeof(git_merge_file_result));
-
- if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_OURS)
- favored = ours;
- else if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS)
- favored = theirs;
- else
- goto done;
-
- if ((out->path = git__strdup(favored->path)) == NULL ||
- (out->ptr = git__malloc(favored->size)) == NULL)
- goto done;
-
- memcpy((char *)out->ptr, favored->ptr, favored->size);
- out->len = favored->size;
- out->mode = favored->mode;
- out->automergeable = 1;
-
-done:
- return 0;
-}
-
-static int merge_file__from_inputs(
- git_merge_file_result *out,
- const git_merge_file_input *ancestor,
- const git_merge_file_input *ours,
- const git_merge_file_input *theirs,
- const git_merge_file_options *given_opts)
-{
- if (merge_file__is_binary(ancestor) ||
- merge_file__is_binary(ours) ||
- merge_file__is_binary(theirs))
- return merge_file__binary(out, ours, theirs, given_opts);
-
- return merge_file__xdiff(out, ancestor, ours, theirs, given_opts);
-}
-
-static git_merge_file_input *git_merge_file__normalize_inputs(
- git_merge_file_input *out,
- const git_merge_file_input *given)
-{
- memcpy(out, given, sizeof(git_merge_file_input));
-
- if (!out->path)
- out->path = "file.txt";
-
- if (!out->mode)
- out->mode = 0100644;
-
- return out;
-}
-
-int git_merge_file(
- git_merge_file_result *out,
- const git_merge_file_input *ancestor,
- const git_merge_file_input *ours,
- const git_merge_file_input *theirs,
- const git_merge_file_options *options)
-{
- git_merge_file_input inputs[3] = { {0} };
-
- assert(out && ours && theirs);
-
- memset(out, 0x0, sizeof(git_merge_file_result));
-
- if (ancestor)
- ancestor = git_merge_file__normalize_inputs(&inputs[0], ancestor);
-
- ours = git_merge_file__normalize_inputs(&inputs[1], ours);
- theirs = git_merge_file__normalize_inputs(&inputs[2], theirs);
-
- return merge_file__from_inputs(out, ancestor, ours, theirs, options);
-}
-
-int git_merge_file_from_index(
- git_merge_file_result *out,
- git_repository *repo,
- const git_index_entry *ancestor,
- const git_index_entry *ours,
- const git_index_entry *theirs,
- const git_merge_file_options *options)
-{
- git_merge_file_input *ancestor_ptr = NULL,
- ancestor_input = {0}, our_input = {0}, their_input = {0};
- git_odb *odb = NULL;
- git_odb_object *odb_object[3] = { 0 };
- int error = 0;
-
- assert(out && repo && ours && theirs);
-
- memset(out, 0x0, sizeof(git_merge_file_result));
-
- if ((error = git_repository_odb(&odb, repo)) < 0)
- goto done;
-
- if (ancestor) {
- if ((error = git_merge_file__input_from_index(
- &ancestor_input, &odb_object[0], odb, ancestor)) < 0)
- goto done;
-
- ancestor_ptr = &ancestor_input;
- }
-
- if ((error = git_merge_file__input_from_index(
- &our_input, &odb_object[1], odb, ours)) < 0 ||
- (error = git_merge_file__input_from_index(
- &their_input, &odb_object[2], odb, theirs)) < 0)
- goto done;
-
- error = merge_file__from_inputs(out,
- ancestor_ptr, &our_input, &their_input, options);
-
-done:
- git_odb_object_free(odb_object[0]);
- git_odb_object_free(odb_object[1]);
- git_odb_object_free(odb_object[2]);
- git_odb_free(odb);
-
- return error;
-}
-
-void git_merge_file_result_free(git_merge_file_result *result)
-{
- if (result == NULL)
- return;
-
- git__free((char *)result->path);
- git__free((char *)result->ptr);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "message.h"
-
-static size_t line_length_without_trailing_spaces(const char *line, size_t len)
-{
- while (len) {
- unsigned char c = line[len - 1];
- if (!git__isspace(c))
- break;
- len--;
- }
-
- return len;
-}
-
-/* Greatly inspired from git.git "stripspace" */
-/* see https://github.com/git/git/blob/497215d8811ac7b8955693ceaad0899ecd894ed2/builtin/stripspace.c#L4-67 */
-int git_message_prettify(git_buf *message_out, const char *message, int strip_comments, char comment_char)
-{
- const size_t message_len = strlen(message);
-
- int consecutive_empty_lines = 0;
- size_t i, line_length, rtrimmed_line_length;
- char *next_newline;
-
- git_buf_sanitize(message_out);
-
- for (i = 0; i < strlen(message); i += line_length) {
- next_newline = memchr(message + i, '\n', message_len - i);
-
- if (next_newline != NULL) {
- line_length = next_newline - (message + i) + 1;
- } else {
- line_length = message_len - i;
- }
-
- if (strip_comments && line_length && message[i] == comment_char)
- continue;
-
- rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length);
-
- if (!rtrimmed_line_length) {
- consecutive_empty_lines++;
- continue;
- }
-
- if (consecutive_empty_lines > 0 && message_out->size > 0)
- git_buf_putc(message_out, '\n');
-
- consecutive_empty_lines = 0;
- git_buf_put(message_out, message + i, rtrimmed_line_length);
- git_buf_putc(message_out, '\n');
- }
-
- return git_buf_oom(message_out) ? -1 : 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_message_h__
-#define INCLUDE_message_h__
-
-#include "git2/message.h"
-#include "buffer.h"
-
-int git_message__prettify(git_buf *message_out, const char *message, int strip_comments);
-
-#endif /* INCLUDE_message_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "mwindow.h"
-#include "vector.h"
-#include "fileops.h"
-#include "map.h"
-#include "global.h"
-#include "strmap.h"
-#include "pack.h"
-
-GIT__USE_STRMAP
-
-#define DEFAULT_WINDOW_SIZE \
- (sizeof(void*) >= 8 \
- ? 1 * 1024 * 1024 * 1024 \
- : 32 * 1024 * 1024)
-
-#define DEFAULT_MAPPED_LIMIT \
- ((1024 * 1024) * (sizeof(void*) >= 8 ? 8192ULL : 256UL))
-
-size_t git_mwindow__window_size = DEFAULT_WINDOW_SIZE;
-size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT;
-
-/* Whenever you want to read or modify this, grab git__mwindow_mutex */
-static git_mwindow_ctl mem_ctl;
-
-/* Global list of mwindow files, to open packs once across repos */
-git_strmap *git__pack_cache = NULL;
-
-static void git_mwindow_files_free(void)
-{
- git_strmap *tmp = git__pack_cache;
-
- git__pack_cache = NULL;
- git_strmap_free(tmp);
-}
-
-int git_mwindow_global_init(void)
-{
- assert(!git__pack_cache);
-
- git__on_shutdown(git_mwindow_files_free);
- return git_strmap_alloc(&git__pack_cache);
-}
-
-int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
-{
- int error;
- char *packname;
- git_strmap_iter pos;
- struct git_pack_file *pack;
-
- if ((error = git_packfile__name(&packname, path)) < 0)
- return error;
-
- if (git_mutex_lock(&git__mwindow_mutex) < 0) {
- giterr_set(GITERR_OS, "failed to lock mwindow mutex");
- return -1;
- }
-
- pos = git_strmap_lookup_index(git__pack_cache, packname);
- git__free(packname);
-
- if (git_strmap_valid_index(git__pack_cache, pos)) {
- pack = git_strmap_value_at(git__pack_cache, pos);
- git_atomic_inc(&pack->refcount);
-
- git_mutex_unlock(&git__mwindow_mutex);
- *out = pack;
- return 0;
- }
-
- /* If we didn't find it, we need to create it */
- if ((error = git_packfile_alloc(&pack, path)) < 0) {
- git_mutex_unlock(&git__mwindow_mutex);
- return error;
- }
-
- git_atomic_inc(&pack->refcount);
-
- git_strmap_insert(git__pack_cache, pack->pack_name, pack, error);
- git_mutex_unlock(&git__mwindow_mutex);
-
- if (error < 0) {
- git_packfile_free(pack);
- return -1;
- }
-
- *out = pack;
- return 0;
-}
-
-void git_mwindow_put_pack(struct git_pack_file *pack)
-{
- int count;
- git_strmap_iter pos;
-
- if (git_mutex_lock(&git__mwindow_mutex) < 0)
- return;
-
- /* put before get would be a corrupted state */
- assert(git__pack_cache);
-
- pos = git_strmap_lookup_index(git__pack_cache, pack->pack_name);
- /* if we cannot find it, the state is corrupted */
- assert(git_strmap_valid_index(git__pack_cache, pos));
-
- count = git_atomic_dec(&pack->refcount);
- if (count == 0) {
- git_strmap_delete_at(git__pack_cache, pos);
- git_packfile_free(pack);
- }
-
- git_mutex_unlock(&git__mwindow_mutex);
- return;
-}
-
-void git_mwindow_free_all(git_mwindow_file *mwf)
-{
- if (git_mutex_lock(&git__mwindow_mutex)) {
- giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
- return;
- }
-
- git_mwindow_free_all_locked(mwf);
-
- git_mutex_unlock(&git__mwindow_mutex);
-}
-
-/*
- * Free all the windows in a sequence, typically because we're done
- * with the file
- */
-void git_mwindow_free_all_locked(git_mwindow_file *mwf)
-{
- git_mwindow_ctl *ctl = &mem_ctl;
- size_t i;
-
- /*
- * Remove these windows from the global list
- */
- for (i = 0; i < ctl->windowfiles.length; ++i){
- if (git_vector_get(&ctl->windowfiles, i) == mwf) {
- git_vector_remove(&ctl->windowfiles, i);
- break;
- }
- }
-
- if (ctl->windowfiles.length == 0) {
- git_vector_free(&ctl->windowfiles);
- ctl->windowfiles.contents = NULL;
- }
-
- while (mwf->windows) {
- git_mwindow *w = mwf->windows;
- assert(w->inuse_cnt == 0);
-
- ctl->mapped -= w->window_map.len;
- ctl->open_windows--;
-
- git_futils_mmap_free(&w->window_map);
-
- mwf->windows = w->next;
- git__free(w);
- }
-}
-
-/*
- * Check if a window 'win' contains the address 'offset'
- */
-int git_mwindow_contains(git_mwindow *win, git_off_t offset)
-{
- git_off_t win_off = win->offset;
- return win_off <= offset
- && offset <= (git_off_t)(win_off + win->window_map.len);
-}
-
-/*
- * Find the least-recently-used window in a file
- */
-static void git_mwindow_scan_lru(
- git_mwindow_file *mwf,
- git_mwindow **lru_w,
- git_mwindow **lru_l)
-{
- git_mwindow *w, *w_l;
-
- for (w_l = NULL, w = mwf->windows; w; w = w->next) {
- if (!w->inuse_cnt) {
- /*
- * If the current one is more recent than the last one,
- * store it in the output parameter. If lru_w is NULL,
- * it's the first loop, so store it as well.
- */
- if (!*lru_w || w->last_used < (*lru_w)->last_used) {
- *lru_w = w;
- *lru_l = w_l;
- }
- }
- w_l = w;
- }
-}
-
-/*
- * Close the least recently used window. You should check to see if
- * the file descriptors need closing from time to time. Called under
- * lock from new_window.
- */
-static int git_mwindow_close_lru(git_mwindow_file *mwf)
-{
- git_mwindow_ctl *ctl = &mem_ctl;
- size_t i;
- git_mwindow *lru_w = NULL, *lru_l = NULL, **list = &mwf->windows;
-
- /* FIXME: Does this give us any advantage? */
- if(mwf->windows)
- git_mwindow_scan_lru(mwf, &lru_w, &lru_l);
-
- for (i = 0; i < ctl->windowfiles.length; ++i) {
- git_mwindow *last = lru_w;
- git_mwindow_file *cur = git_vector_get(&ctl->windowfiles, i);
- git_mwindow_scan_lru(cur, &lru_w, &lru_l);
- if (lru_w != last)
- list = &cur->windows;
- }
-
- if (!lru_w) {
- giterr_set(GITERR_OS, "Failed to close memory window. Couldn't find LRU");
- return -1;
- }
-
- ctl->mapped -= lru_w->window_map.len;
- git_futils_mmap_free(&lru_w->window_map);
-
- if (lru_l)
- lru_l->next = lru_w->next;
- else
- *list = lru_w->next;
-
- git__free(lru_w);
- ctl->open_windows--;
-
- return 0;
-}
-
-/* This gets called under lock from git_mwindow_open */
-static git_mwindow *new_window(
- git_mwindow_file *mwf,
- git_file fd,
- git_off_t size,
- git_off_t offset)
-{
- git_mwindow_ctl *ctl = &mem_ctl;
- size_t walign = git_mwindow__window_size / 2;
- git_off_t len;
- git_mwindow *w;
-
- w = git__malloc(sizeof(*w));
-
- if (w == NULL)
- return NULL;
-
- memset(w, 0x0, sizeof(*w));
- w->offset = (offset / walign) * walign;
-
- len = size - w->offset;
- if (len > (git_off_t)git_mwindow__window_size)
- len = (git_off_t)git_mwindow__window_size;
-
- ctl->mapped += (size_t)len;
-
- while (git_mwindow__mapped_limit < ctl->mapped &&
- git_mwindow_close_lru(mwf) == 0) /* nop */;
-
- /*
- * We treat `mapped_limit` as a soft limit. If we can't find a
- * window to close and are above the limit, we still mmap the new
- * window.
- */
-
- if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
- /*
- * The first error might be down to memory fragmentation even if
- * we're below our soft limits, so free up what we can and try again.
- */
-
- while (git_mwindow_close_lru(mwf) == 0)
- /* nop */;
-
- if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
- git__free(w);
- return NULL;
- }
- }
-
- ctl->mmap_calls++;
- ctl->open_windows++;
-
- if (ctl->mapped > ctl->peak_mapped)
- ctl->peak_mapped = ctl->mapped;
-
- if (ctl->open_windows > ctl->peak_open_windows)
- ctl->peak_open_windows = ctl->open_windows;
-
- return w;
-}
-
-/*
- * Open a new window, closing the least recenty used until we have
- * enough space. Don't forget to add it to your list
- */
-unsigned char *git_mwindow_open(
- git_mwindow_file *mwf,
- git_mwindow **cursor,
- git_off_t offset,
- size_t extra,
- unsigned int *left)
-{
- git_mwindow_ctl *ctl = &mem_ctl;
- git_mwindow *w = *cursor;
-
- if (git_mutex_lock(&git__mwindow_mutex)) {
- giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
- return NULL;
- }
-
- if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {
- if (w) {
- w->inuse_cnt--;
- }
-
- for (w = mwf->windows; w; w = w->next) {
- if (git_mwindow_contains(w, offset) &&
- git_mwindow_contains(w, offset + extra))
- break;
- }
-
- /*
- * If there isn't a suitable window, we need to create a new
- * one.
- */
- if (!w) {
- w = new_window(mwf, mwf->fd, mwf->size, offset);
- if (w == NULL) {
- git_mutex_unlock(&git__mwindow_mutex);
- return NULL;
- }
- w->next = mwf->windows;
- mwf->windows = w;
- }
- }
-
- /* If we changed w, store it in the cursor */
- if (w != *cursor) {
- w->last_used = ctl->used_ctr++;
- w->inuse_cnt++;
- *cursor = w;
- }
-
- offset -= w->offset;
-
- if (left)
- *left = (unsigned int)(w->window_map.len - offset);
-
- git_mutex_unlock(&git__mwindow_mutex);
- return (unsigned char *) w->window_map.data + offset;
-}
-
-int git_mwindow_file_register(git_mwindow_file *mwf)
-{
- git_mwindow_ctl *ctl = &mem_ctl;
- int ret;
-
- if (git_mutex_lock(&git__mwindow_mutex)) {
- giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
- return -1;
- }
-
- if (ctl->windowfiles.length == 0 &&
- git_vector_init(&ctl->windowfiles, 8, NULL) < 0) {
- git_mutex_unlock(&git__mwindow_mutex);
- return -1;
- }
-
- ret = git_vector_insert(&ctl->windowfiles, mwf);
- git_mutex_unlock(&git__mwindow_mutex);
-
- return ret;
-}
-
-void git_mwindow_file_deregister(git_mwindow_file *mwf)
-{
- git_mwindow_ctl *ctl = &mem_ctl;
- git_mwindow_file *cur;
- size_t i;
-
- if (git_mutex_lock(&git__mwindow_mutex))
- return;
-
- git_vector_foreach(&ctl->windowfiles, i, cur) {
- if (cur == mwf) {
- git_vector_remove(&ctl->windowfiles, i);
- git_mutex_unlock(&git__mwindow_mutex);
- return;
- }
- }
- git_mutex_unlock(&git__mwindow_mutex);
-}
-
-void git_mwindow_close(git_mwindow **window)
-{
- git_mwindow *w = *window;
- if (w) {
- if (git_mutex_lock(&git__mwindow_mutex)) {
- giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
- return;
- }
-
- w->inuse_cnt--;
- git_mutex_unlock(&git__mwindow_mutex);
- *window = NULL;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_mwindow__
-#define INCLUDE_mwindow__
-
-#include "map.h"
-#include "vector.h"
-
-typedef struct git_mwindow {
- struct git_mwindow *next;
- git_map window_map;
- git_off_t offset;
- size_t last_used;
- size_t inuse_cnt;
-} git_mwindow;
-
-typedef struct git_mwindow_file {
- git_mwindow *windows;
- int fd;
- git_off_t size;
-} git_mwindow_file;
-
-typedef struct git_mwindow_ctl {
- size_t mapped;
- unsigned int open_windows;
- unsigned int mmap_calls;
- unsigned int peak_open_windows;
- size_t peak_mapped;
- size_t used_ctr;
- git_vector windowfiles;
-} git_mwindow_ctl;
-
-int git_mwindow_contains(git_mwindow *win, git_off_t offset);
-void git_mwindow_free_all(git_mwindow_file *mwf); /* locks */
-void git_mwindow_free_all_locked(git_mwindow_file *mwf); /* run under lock */
-unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, size_t extra, unsigned int *left);
-int git_mwindow_file_register(git_mwindow_file *mwf);
-void git_mwindow_file_deregister(git_mwindow_file *mwf);
-void git_mwindow_close(git_mwindow **w_cursor);
-
-extern int git_mwindow_global_init(void);
-
-struct git_pack_file; /* just declaration to avoid cyclical includes */
-int git_mwindow_get_pack(struct git_pack_file **out, const char *path);
-void git_mwindow_put_pack(struct git_pack_file *pack);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include <ctype.h>
-#include "git2/errors.h"
-
-#include "common.h"
-#include "netops.h"
-#include "posix.h"
-#include "buffer.h"
-#include "http_parser.h"
-#include "global.h"
-
-int gitno_recv(gitno_buffer *buf)
-{
- return buf->recv(buf);
-}
-
-void gitno_buffer_setup_callback(
- gitno_buffer *buf,
- char *data,
- size_t len,
- int (*recv)(gitno_buffer *buf), void *cb_data)
-{
- memset(data, 0x0, len);
- buf->data = data;
- buf->len = len;
- buf->offset = 0;
- buf->recv = recv;
- buf->cb_data = cb_data;
-}
-
-static int recv_stream(gitno_buffer *buf)
-{
- git_stream *io = (git_stream *) buf->cb_data;
- int ret;
-
- ret = git_stream_read(io, buf->data + buf->offset, buf->len - buf->offset);
- if (ret < 0)
- return -1;
-
- buf->offset += ret;
- return ret;
-}
-
-void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len)
-{
- memset(data, 0x0, len);
- buf->data = data;
- buf->len = len;
- buf->offset = 0;
- buf->recv = recv_stream;
- buf->cb_data = st;
-}
-
-/* Consume up to ptr and move the rest of the buffer to the beginning */
-void gitno_consume(gitno_buffer *buf, const char *ptr)
-{
- size_t consumed;
-
- assert(ptr - buf->data >= 0);
- assert(ptr - buf->data <= (int) buf->len);
-
- consumed = ptr - buf->data;
-
- memmove(buf->data, ptr, buf->offset - consumed);
- memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);
- buf->offset -= consumed;
-}
-
-/* Consume const bytes and move the rest of the buffer to the beginning */
-void gitno_consume_n(gitno_buffer *buf, size_t cons)
-{
- memmove(buf->data, buf->data + cons, buf->len - buf->offset);
- memset(buf->data + cons, 0x0, buf->len - buf->offset);
- buf->offset -= cons;
-}
-
-/* Match host names according to RFC 2818 rules */
-int gitno__match_host(const char *pattern, const char *host)
-{
- for (;;) {
- char c = git__tolower(*pattern++);
-
- if (c == '\0')
- return *host ? -1 : 0;
-
- if (c == '*') {
- c = *pattern;
- /* '*' at the end matches everything left */
- if (c == '\0')
- return 0;
-
- /*
- * We've found a pattern, so move towards the next matching
- * char. The '.' is handled specially because wildcards aren't
- * allowed to cross subdomains.
- */
-
- while(*host) {
- char h = git__tolower(*host);
- if (c == h)
- return gitno__match_host(pattern, host++);
- if (h == '.')
- return gitno__match_host(pattern, host);
- host++;
- }
- return -1;
- }
-
- if (c != git__tolower(*host++))
- return -1;
- }
-
- return -1;
-}
-
-static const char *prefix_http = "http://";
-static const char *prefix_https = "https://";
-
-int gitno_connection_data_from_url(
- gitno_connection_data *data,
- const char *url,
- const char *service_suffix)
-{
- int error = -1;
- const char *default_port = NULL, *path_search_start = NULL;
- char *original_host = NULL;
-
- /* service_suffix is optional */
- assert(data && url);
-
- /* Save these for comparison later */
- original_host = data->host;
- data->host = NULL;
- gitno_connection_data_free_ptrs(data);
-
- if (!git__prefixcmp(url, prefix_http)) {
- path_search_start = url + strlen(prefix_http);
- default_port = "80";
-
- if (data->use_ssl) {
- giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed");
- goto cleanup;
- }
- } else if (!git__prefixcmp(url, prefix_https)) {
- path_search_start = url + strlen(prefix_https);
- default_port = "443";
- data->use_ssl = true;
- } else if (url[0] == '/')
- default_port = data->use_ssl ? "443" : "80";
-
- if (!default_port) {
- giterr_set(GITERR_NET, "Unrecognized URL prefix");
- goto cleanup;
- }
-
- error = gitno_extract_url_parts(
- &data->host, &data->port, &data->path, &data->user, &data->pass,
- url, default_port);
-
- if (url[0] == '/') {
- /* Relative redirect; reuse original host name and port */
- path_search_start = url;
- git__free(data->host);
- data->host = original_host;
- original_host = NULL;
- }
-
- if (!error) {
- const char *path = strchr(path_search_start, '/');
- size_t pathlen = strlen(path);
- size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
-
- if (suffixlen &&
- !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen)) {
- git__free(data->path);
- data->path = git__strndup(path, pathlen - suffixlen);
- } else {
- git__free(data->path);
- data->path = git__strdup(path);
- }
-
- /* Check for errors in the resulting data */
- if (original_host && url[0] != '/' && strcmp(original_host, data->host)) {
- giterr_set(GITERR_NET, "Cross host redirect not allowed");
- error = -1;
- }
- }
-
-cleanup:
- if (original_host) git__free(original_host);
- return error;
-}
-
-void gitno_connection_data_free_ptrs(gitno_connection_data *d)
-{
- git__free(d->host); d->host = NULL;
- git__free(d->port); d->port = NULL;
- git__free(d->path); d->path = NULL;
- git__free(d->user); d->user = NULL;
- git__free(d->pass); d->pass = NULL;
-}
-
-#define hex2c(c) ((c | 32) % 39 - 9)
-static char* unescape(char *str)
-{
- int x, y;
- int len = (int)strlen(str);
-
- for (x=y=0; str[y]; ++x, ++y) {
- if ((str[x] = str[y]) == '%') {
- if (y < len-2 && isxdigit(str[y+1]) && isxdigit(str[y+2])) {
- str[x] = (hex2c(str[y+1]) << 4) + hex2c(str[y+2]);
- y += 2;
- }
- }
- }
- str[x] = '\0';
- return str;
-}
-
-int gitno_extract_url_parts(
- char **host,
- char **port,
- char **path,
- char **username,
- char **password,
- const char *url,
- const char *default_port)
-{
- struct http_parser_url u = {0};
- const char *_host, *_port, *_path, *_userinfo;
-
- if (http_parser_parse_url(url, strlen(url), false, &u)) {
- giterr_set(GITERR_NET, "Malformed URL '%s'", url);
- return GIT_EINVALIDSPEC;
- }
-
- _host = url+u.field_data[UF_HOST].off;
- _port = url+u.field_data[UF_PORT].off;
- _path = url+u.field_data[UF_PATH].off;
- _userinfo = url+u.field_data[UF_USERINFO].off;
-
- if (u.field_set & (1 << UF_HOST)) {
- *host = git__substrdup(_host, u.field_data[UF_HOST].len);
- GITERR_CHECK_ALLOC(*host);
- }
-
- if (u.field_set & (1 << UF_PORT))
- *port = git__substrdup(_port, u.field_data[UF_PORT].len);
- else
- *port = git__strdup(default_port);
- GITERR_CHECK_ALLOC(*port);
-
- if (path) {
- if (u.field_set & (1 << UF_PATH)) {
- *path = git__substrdup(_path, u.field_data[UF_PATH].len);
- GITERR_CHECK_ALLOC(*path);
- } else {
- git__free(*port);
- *port = NULL;
- git__free(*host);
- *host = NULL;
- giterr_set(GITERR_NET, "invalid url, missing path");
- return GIT_EINVALIDSPEC;
- }
- }
-
- if (u.field_set & (1 << UF_USERINFO)) {
- const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len);
- if (colon) {
- *username = unescape(git__substrdup(_userinfo, colon - _userinfo));
- *password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)));
- GITERR_CHECK_ALLOC(*password);
- } else {
- *username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
- }
- GITERR_CHECK_ALLOC(*username);
-
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_netops_h__
-#define INCLUDE_netops_h__
-
-#include "posix.h"
-#include "common.h"
-#include "stream.h"
-
-#ifdef GIT_OPENSSL
-# include <openssl/ssl.h>
-#endif
-
-typedef struct gitno_ssl {
-#ifdef GIT_OPENSSL
- SSL *ssl;
-#else
- size_t dummy;
-#endif
-} gitno_ssl;
-
-/* Represents a socket that may or may not be using SSL */
-typedef struct gitno_socket {
- GIT_SOCKET socket;
- gitno_ssl ssl;
-} gitno_socket;
-
-typedef struct gitno_buffer {
- char *data;
- size_t len;
- size_t offset;
- int (*recv)(struct gitno_buffer *buffer);
- void *cb_data;
-} gitno_buffer;
-
-/* Flags to gitno_connect */
-enum {
- /* Attempt to create an SSL connection. */
- GITNO_CONNECT_SSL = 1,
-};
-
-/**
- * Check if the name in a cert matches the wanted hostname
- *
- * Check if a pattern from a certificate matches the hostname we
- * wanted to connect to according to RFC2818 rules (which specifies
- * HTTP over TLS). Mainly, an asterisk matches anything, but is
- * limited to a single url component.
- *
- * Note that this does not set an error message. It expects the user
- * to provide the message for the user.
- */
-int gitno__match_host(const char *pattern, const char *host);
-
-void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len);
-void gitno_buffer_setup_callback(gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data);
-int gitno_recv(gitno_buffer *buf);
-
-void gitno_consume(gitno_buffer *buf, const char *ptr);
-void gitno_consume_n(gitno_buffer *buf, size_t cons);
-
-typedef struct gitno_connection_data {
- char *host;
- char *port;
- char *path;
- char *user;
- char *pass;
- bool use_ssl;
-} gitno_connection_data;
-
-/*
- * This replaces all the pointers in `data` with freshly-allocated strings,
- * that the caller is responsible for freeing.
- * `gitno_connection_data_free_ptrs` is good for this.
- */
-
-int gitno_connection_data_from_url(
- gitno_connection_data *data,
- const char *url,
- const char *service_suffix);
-
-/* This frees all the pointers IN the struct, but not the struct itself. */
-void gitno_connection_data_free_ptrs(gitno_connection_data *data);
-
-int gitno_extract_url_parts(
- char **host,
- char **port,
- char **path,
- char **username,
- char **password,
- const char *url,
- const char *default_port);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "notes.h"
-
-#include "git2.h"
-#include "refs.h"
-#include "config.h"
-#include "iterator.h"
-#include "signature.h"
-
-static int note_error_notfound(void)
-{
- giterr_set(GITERR_INVALID, "Note could not be found");
- return GIT_ENOTFOUND;
-}
-
-static int find_subtree_in_current_level(
- git_tree **out,
- git_repository *repo,
- git_tree *parent,
- const char *annotated_object_sha,
- int fanout)
-{
- size_t i;
- const git_tree_entry *entry;
-
- *out = NULL;
-
- if (parent == NULL)
- return note_error_notfound();
-
- for (i = 0; i < git_tree_entrycount(parent); i++) {
- entry = git_tree_entry_byindex(parent, i);
-
- if (!git__ishex(git_tree_entry_name(entry)))
- continue;
-
- if (S_ISDIR(git_tree_entry_filemode(entry))
- && strlen(git_tree_entry_name(entry)) == 2
- && !strncmp(git_tree_entry_name(entry), annotated_object_sha + fanout, 2))
- return git_tree_lookup(out, repo, git_tree_entry_id(entry));
-
- /* Not a DIR, so do we have an already existing blob? */
- if (!strcmp(git_tree_entry_name(entry), annotated_object_sha + fanout))
- return GIT_EEXISTS;
- }
-
- return note_error_notfound();
-}
-
-static int find_subtree_r(git_tree **out, git_tree *root,
- git_repository *repo, const char *target, int *fanout)
-{
- int error;
- git_tree *subtree = NULL;
-
- *out = NULL;
-
- error = find_subtree_in_current_level(&subtree, repo, root, target, *fanout);
- if (error == GIT_EEXISTS)
- return git_tree_lookup(out, repo, git_tree_id(root));
-
- if (error < 0)
- return error;
-
- *fanout += 2;
- error = find_subtree_r(out, subtree, repo, target, fanout);
- git_tree_free(subtree);
-
- return error;
-}
-
-static int find_blob(git_oid *blob, git_tree *tree, const char *target)
-{
- size_t i;
- const git_tree_entry *entry;
-
- for (i=0; i<git_tree_entrycount(tree); i++) {
- entry = git_tree_entry_byindex(tree, i);
-
- if (!strcmp(git_tree_entry_name(entry), target)) {
- /* found matching note object - return */
-
- git_oid_cpy(blob, git_tree_entry_id(entry));
- return 0;
- }
- }
-
- return note_error_notfound();
-}
-
-static int tree_write(
- git_tree **out,
- git_repository *repo,
- git_tree *source_tree,
- const git_oid *object_oid,
- const char *treeentry_name,
- unsigned int attributes)
-{
- int error;
- git_treebuilder *tb = NULL;
- const git_tree_entry *entry;
- git_oid tree_oid;
-
- if ((error = git_treebuilder_new(&tb, repo, source_tree)) < 0)
- goto cleanup;
-
- if (object_oid) {
- if ((error = git_treebuilder_insert(
- &entry, tb, treeentry_name, object_oid, attributes)) < 0)
- goto cleanup;
- } else {
- if ((error = git_treebuilder_remove(tb, treeentry_name)) < 0)
- goto cleanup;
- }
-
- if ((error = git_treebuilder_write(&tree_oid, tb)) < 0)
- goto cleanup;
-
- error = git_tree_lookup(out, repo, &tree_oid);
-
-cleanup:
- git_treebuilder_free(tb);
- return error;
-}
-
-static int manipulate_note_in_tree_r(
- git_tree **out,
- git_repository *repo,
- git_tree *parent,
- git_oid *note_oid,
- const char *annotated_object_sha,
- int fanout,
- int (*note_exists_cb)(
- git_tree **out,
- git_repository *repo,
- git_tree *parent,
- git_oid *note_oid,
- const char *annotated_object_sha,
- int fanout,
- int current_error),
- int (*note_notfound_cb)(
- git_tree **out,
- git_repository *repo,
- git_tree *parent,
- git_oid *note_oid,
- const char *annotated_object_sha,
- int fanout,
- int current_error))
-{
- int error;
- git_tree *subtree = NULL, *new = NULL;
- char subtree_name[3];
-
- error = find_subtree_in_current_level(
- &subtree, repo, parent, annotated_object_sha, fanout);
-
- if (error == GIT_EEXISTS) {
- error = note_exists_cb(
- out, repo, parent, note_oid, annotated_object_sha, fanout, error);
- goto cleanup;
- }
-
- if (error == GIT_ENOTFOUND) {
- error = note_notfound_cb(
- out, repo, parent, note_oid, annotated_object_sha, fanout, error);
- goto cleanup;
- }
-
- if (error < 0)
- goto cleanup;
-
- /* An existing fanout has been found, let's dig deeper */
- error = manipulate_note_in_tree_r(
- &new, repo, subtree, note_oid, annotated_object_sha,
- fanout + 2, note_exists_cb, note_notfound_cb);
-
- if (error < 0)
- goto cleanup;
-
- strncpy(subtree_name, annotated_object_sha + fanout, 2);
- subtree_name[2] = '\0';
-
- error = tree_write(out, repo, parent, git_tree_id(new),
- subtree_name, GIT_FILEMODE_TREE);
-
-
-cleanup:
- git_tree_free(new);
- git_tree_free(subtree);
- return error;
-}
-
-static int remove_note_in_tree_eexists_cb(
- git_tree **out,
- git_repository *repo,
- git_tree *parent,
- git_oid *note_oid,
- const char *annotated_object_sha,
- int fanout,
- int current_error)
-{
- GIT_UNUSED(note_oid);
- GIT_UNUSED(current_error);
-
- return tree_write(out, repo, parent, NULL, annotated_object_sha + fanout, 0);
-}
-
-static int remove_note_in_tree_enotfound_cb(
- git_tree **out,
- git_repository *repo,
- git_tree *parent,
- git_oid *note_oid,
- const char *annotated_object_sha,
- int fanout,
- int current_error)
-{
- GIT_UNUSED(out);
- GIT_UNUSED(repo);
- GIT_UNUSED(parent);
- GIT_UNUSED(note_oid);
- GIT_UNUSED(fanout);
-
- giterr_set(GITERR_REPOSITORY, "Object '%s' has no note", annotated_object_sha);
- return current_error;
-}
-
-static int insert_note_in_tree_eexists_cb(git_tree **out,
- git_repository *repo,
- git_tree *parent,
- git_oid *note_oid,
- const char *annotated_object_sha,
- int fanout,
- int current_error)
-{
- GIT_UNUSED(out);
- GIT_UNUSED(repo);
- GIT_UNUSED(parent);
- GIT_UNUSED(note_oid);
- GIT_UNUSED(fanout);
-
- giterr_set(GITERR_REPOSITORY, "Note for '%s' exists already", annotated_object_sha);
- return current_error;
-}
-
-static int insert_note_in_tree_enotfound_cb(git_tree **out,
- git_repository *repo,
- git_tree *parent,
- git_oid *note_oid,
- const char *annotated_object_sha,
- int fanout,
- int current_error)
-{
- GIT_UNUSED(current_error);
-
- /* No existing fanout at this level, insert in place */
- return tree_write(
- out,
- repo,
- parent,
- note_oid,
- annotated_object_sha + fanout,
- GIT_FILEMODE_BLOB);
-}
-
-static int note_write(git_oid *out,
- git_repository *repo,
- const git_signature *author,
- const git_signature *committer,
- const char *notes_ref,
- const char *note,
- git_tree *commit_tree,
- const char *target,
- git_commit **parents,
- int allow_note_overwrite)
-{
- int error;
- git_oid oid;
- git_tree *tree = NULL;
-
- // TODO: should we apply filters?
- /* create note object */
- if ((error = git_blob_create_frombuffer(&oid, repo, note, strlen(note))) < 0)
- goto cleanup;
-
- if ((error = manipulate_note_in_tree_r(
- &tree, repo, commit_tree, &oid, target, 0,
- allow_note_overwrite ? insert_note_in_tree_enotfound_cb : insert_note_in_tree_eexists_cb,
- insert_note_in_tree_enotfound_cb)) < 0)
- goto cleanup;
-
- if (out)
- git_oid_cpy(out, &oid);
-
- error = git_commit_create(&oid, repo, notes_ref, author, committer,
- NULL, GIT_NOTES_DEFAULT_MSG_ADD,
- tree, *parents == NULL ? 0 : 1, (const git_commit **) parents);
-
-cleanup:
- git_tree_free(tree);
- return error;
-}
-
-static int note_new(
- git_note **out,
- git_oid *note_oid,
- git_commit *commit,
- git_blob *blob)
-{
- git_note *note = NULL;
-
- note = git__malloc(sizeof(git_note));
- GITERR_CHECK_ALLOC(note);
-
- git_oid_cpy(¬e->id, note_oid);
-
- if (git_signature_dup(¬e->author, git_commit_author(commit)) < 0 ||
- git_signature_dup(¬e->committer, git_commit_committer(commit)) < 0)
- return -1;
-
- note->message = git__strndup(git_blob_rawcontent(blob), git_blob_rawsize(blob));
- GITERR_CHECK_ALLOC(note->message);
-
- *out = note;
- return 0;
-}
-
-static int note_lookup(
- git_note **out,
- git_repository *repo,
- git_commit *commit,
- git_tree *tree,
- const char *target)
-{
- int error, fanout = 0;
- git_oid oid;
- git_blob *blob = NULL;
- git_note *note = NULL;
- git_tree *subtree = NULL;
-
- if ((error = find_subtree_r(&subtree, tree, repo, target, &fanout)) < 0)
- goto cleanup;
-
- if ((error = find_blob(&oid, subtree, target + fanout)) < 0)
- goto cleanup;
-
- if ((error = git_blob_lookup(&blob, repo, &oid)) < 0)
- goto cleanup;
-
- if ((error = note_new(¬e, &oid, commit, blob)) < 0)
- goto cleanup;
-
- *out = note;
-
-cleanup:
- git_tree_free(subtree);
- git_blob_free(blob);
- return error;
-}
-
-static int note_remove(git_repository *repo,
- const git_signature *author, const git_signature *committer,
- const char *notes_ref, git_tree *tree,
- const char *target, git_commit **parents)
-{
- int error;
- git_tree *tree_after_removal = NULL;
- git_oid oid;
-
- if ((error = manipulate_note_in_tree_r(
- &tree_after_removal, repo, tree, NULL, target, 0,
- remove_note_in_tree_eexists_cb, remove_note_in_tree_enotfound_cb)) < 0)
- goto cleanup;
-
- error = git_commit_create(&oid, repo, notes_ref, author, committer,
- NULL, GIT_NOTES_DEFAULT_MSG_RM,
- tree_after_removal,
- *parents == NULL ? 0 : 1,
- (const git_commit **) parents);
-
-cleanup:
- git_tree_free(tree_after_removal);
- return error;
-}
-
-static int note_get_default_ref(char **out, git_repository *repo)
-{
- git_config *cfg;
- int ret = git_repository_config__weakptr(&cfg, repo);
-
- *out = (ret != 0) ? NULL : git_config__get_string_force(
- cfg, "core.notesref", GIT_NOTES_DEFAULT_REF);
-
- return ret;
-}
-
-static int normalize_namespace(char **out, git_repository *repo, const char *notes_ref)
-{
- if (notes_ref) {
- *out = git__strdup(notes_ref);
- GITERR_CHECK_ALLOC(*out);
- return 0;
- }
-
- return note_get_default_ref(out, repo);
-}
-
-static int retrieve_note_tree_and_commit(
- git_tree **tree_out,
- git_commit **commit_out,
- char **notes_ref_out,
- git_repository *repo,
- const char *notes_ref)
-{
- int error;
- git_oid oid;
-
- if ((error = normalize_namespace(notes_ref_out, repo, notes_ref)) < 0)
- return error;
-
- if ((error = git_reference_name_to_id(&oid, repo, *notes_ref_out)) < 0)
- return error;
-
- if (git_commit_lookup(commit_out, repo, &oid) < 0)
- return error;
-
- if ((error = git_commit_tree(tree_out, *commit_out)) < 0)
- return error;
-
- return 0;
-}
-
-int git_note_read(git_note **out, git_repository *repo,
- const char *notes_ref_in, const git_oid *oid)
-{
- int error;
- char *target = NULL, *notes_ref = NULL;
- git_tree *tree = NULL;
- git_commit *commit = NULL;
-
- target = git_oid_allocfmt(oid);
- GITERR_CHECK_ALLOC(target);
-
- if (!(error = retrieve_note_tree_and_commit(
- &tree, &commit, ¬es_ref, repo, notes_ref_in)))
- error = note_lookup(out, repo, commit, tree, target);
-
- git__free(notes_ref);
- git__free(target);
- git_tree_free(tree);
- git_commit_free(commit);
- return error;
-}
-
-int git_note_create(
- git_oid *out,
- git_repository *repo,
- const char *notes_ref_in,
- const git_signature *author,
- const git_signature *committer,
- const git_oid *oid,
- const char *note,
- int allow_note_overwrite)
-{
- int error;
- char *target = NULL, *notes_ref = NULL;
- git_commit *commit = NULL;
- git_tree *tree = NULL;
-
- target = git_oid_allocfmt(oid);
- GITERR_CHECK_ALLOC(target);
-
- error = retrieve_note_tree_and_commit(&tree, &commit, ¬es_ref, repo, notes_ref_in);
-
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- error = note_write(out, repo, author, committer, notes_ref,
- note, tree, target, &commit, allow_note_overwrite);
-
-cleanup:
- git__free(notes_ref);
- git__free(target);
- git_commit_free(commit);
- git_tree_free(tree);
- return error;
-}
-
-int git_note_remove(git_repository *repo, const char *notes_ref_in,
- const git_signature *author, const git_signature *committer,
- const git_oid *oid)
-{
- int error;
- char *target = NULL, *notes_ref;
- git_commit *commit = NULL;
- git_tree *tree = NULL;
-
- target = git_oid_allocfmt(oid);
- GITERR_CHECK_ALLOC(target);
-
- if (!(error = retrieve_note_tree_and_commit(
- &tree, &commit, ¬es_ref, repo, notes_ref_in)))
- error = note_remove(
- repo, author, committer, notes_ref, tree, target, &commit);
-
- git__free(notes_ref);
- git__free(target);
- git_commit_free(commit);
- git_tree_free(tree);
- return error;
-}
-
-int git_note_default_ref(git_buf *out, git_repository *repo)
-{
- char *default_ref;
- int error;
-
- assert(out && repo);
-
- git_buf_sanitize(out);
-
- if ((error = note_get_default_ref(&default_ref, repo)) < 0)
- return error;
-
- git_buf_attach(out, default_ref, strlen(default_ref));
- return 0;
-}
-
-const git_signature *git_note_committer(const git_note *note)
-{
- assert(note);
- return note->committer;
-}
-
-const git_signature *git_note_author(const git_note *note)
-{
- assert(note);
- return note->author;
-}
-
-const char * git_note_message(const git_note *note)
-{
- assert(note);
- return note->message;
-}
-
-const git_oid * git_note_id(const git_note *note)
-{
- assert(note);
- return ¬e->id;
-}
-
-void git_note_free(git_note *note)
-{
- if (note == NULL)
- return;
-
- git_signature_free(note->committer);
- git_signature_free(note->author);
- git__free(note->message);
- git__free(note);
-}
-
-static int process_entry_path(
- const char* entry_path,
- git_oid *annotated_object_id)
-{
- int error = 0;
- size_t i = 0, j = 0, len;
- git_buf buf = GIT_BUF_INIT;
-
- if ((error = git_buf_puts(&buf, entry_path)) < 0)
- goto cleanup;
-
- len = git_buf_len(&buf);
-
- while (i < len) {
- if (buf.ptr[i] == '/') {
- i++;
- continue;
- }
-
- if (git__fromhex(buf.ptr[i]) < 0) {
- /* This is not a note entry */
- goto cleanup;
- }
-
- if (i != j)
- buf.ptr[j] = buf.ptr[i];
-
- i++;
- j++;
- }
-
- buf.ptr[j] = '\0';
- buf.size = j;
-
- if (j != GIT_OID_HEXSZ) {
- /* This is not a note entry */
- goto cleanup;
- }
-
- error = git_oid_fromstr(annotated_object_id, buf.ptr);
-
-cleanup:
- git_buf_free(&buf);
- return error;
-}
-
-int git_note_foreach(
- git_repository *repo,
- const char *notes_ref,
- git_note_foreach_cb note_cb,
- void *payload)
-{
- int error;
- git_note_iterator *iter = NULL;
- git_oid note_id, annotated_id;
-
- if ((error = git_note_iterator_new(&iter, repo, notes_ref)) < 0)
- return error;
-
- while (!(error = git_note_next(¬e_id, &annotated_id, iter))) {
- if ((error = note_cb(¬e_id, &annotated_id, payload)) != 0) {
- giterr_set_after_callback(error);
- break;
- }
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- git_note_iterator_free(iter);
- return error;
-}
-
-
-void git_note_iterator_free(git_note_iterator *it)
-{
- if (it == NULL)
- return;
-
- git_iterator_free(it);
-}
-
-
-int git_note_iterator_new(
- git_note_iterator **it,
- git_repository *repo,
- const char *notes_ref_in)
-{
- int error;
- git_commit *commit = NULL;
- git_tree *tree = NULL;
- char *notes_ref;
-
- error = retrieve_note_tree_and_commit(&tree, &commit, ¬es_ref, repo, notes_ref_in);
- if (error < 0)
- goto cleanup;
-
- if ((error = git_iterator_for_tree(it, tree, NULL)) < 0)
- git_iterator_free(*it);
-
-cleanup:
- git__free(notes_ref);
- git_tree_free(tree);
- git_commit_free(commit);
-
- return error;
-}
-
-int git_note_next(
- git_oid* note_id,
- git_oid* annotated_id,
- git_note_iterator *it)
-{
- int error;
- const git_index_entry *item;
-
- if ((error = git_iterator_current(&item, it)) < 0)
- return error;
-
- git_oid_cpy(note_id, &item->id);
-
- if (!(error = process_entry_path(item->path, annotated_id)))
- git_iterator_advance(NULL, it);
-
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_note_h__
-#define INCLUDE_note_h__
-
-#include "common.h"
-
-#include "git2/oid.h"
-#include "git2/types.h"
-
-#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
-
-#define GIT_NOTES_DEFAULT_MSG_ADD \
- "Notes added by 'git_note_create' from libgit2"
-
-#define GIT_NOTES_DEFAULT_MSG_RM \
- "Notes removed by 'git_note_remove' from libgit2"
-
-struct git_note {
- git_oid id;
-
- git_signature *author;
- git_signature *committer;
-
- char *message;
-};
-
-#endif /* INCLUDE_notes_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "git2/object.h"
-
-#include "common.h"
-#include "repository.h"
-
-#include "commit.h"
-#include "tree.h"
-#include "blob.h"
-#include "oid.h"
-#include "tag.h"
-
-bool git_object__strict_input_validation = true;
-
-typedef struct {
- const char *str; /* type name string */
- size_t size; /* size in bytes of the object structure */
-
- int (*parse)(void *self, git_odb_object *obj);
- void (*free)(void *self);
-} git_object_def;
-
-static git_object_def git_objects_table[] = {
- /* 0 = GIT_OBJ__EXT1 */
- { "", 0, NULL, NULL },
-
- /* 1 = GIT_OBJ_COMMIT */
- { "commit", sizeof(git_commit), git_commit__parse, git_commit__free },
-
- /* 2 = GIT_OBJ_TREE */
- { "tree", sizeof(git_tree), git_tree__parse, git_tree__free },
-
- /* 3 = GIT_OBJ_BLOB */
- { "blob", sizeof(git_blob), git_blob__parse, git_blob__free },
-
- /* 4 = GIT_OBJ_TAG */
- { "tag", sizeof(git_tag), git_tag__parse, git_tag__free },
-
- /* 5 = GIT_OBJ__EXT2 */
- { "", 0, NULL, NULL },
- /* 6 = GIT_OBJ_OFS_DELTA */
- { "OFS_DELTA", 0, NULL, NULL },
- /* 7 = GIT_OBJ_REF_DELTA */
- { "REF_DELTA", 0, NULL, NULL },
-};
-
-int git_object__from_odb_object(
- git_object **object_out,
- git_repository *repo,
- git_odb_object *odb_obj,
- git_otype type)
-{
- int error;
- size_t object_size;
- git_object_def *def;
- git_object *object = NULL;
-
- assert(object_out);
- *object_out = NULL;
-
- /* Validate type match */
- if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) {
- giterr_set(GITERR_INVALID,
- "The requested type does not match the type in the ODB");
- return GIT_ENOTFOUND;
- }
-
- if ((object_size = git_object__size(odb_obj->cached.type)) == 0) {
- giterr_set(GITERR_INVALID, "The requested type is invalid");
- return GIT_ENOTFOUND;
- }
-
- /* Allocate and initialize base object */
- object = git__calloc(1, object_size);
- GITERR_CHECK_ALLOC(object);
-
- git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
- object->cached.type = odb_obj->cached.type;
- object->cached.size = odb_obj->cached.size;
- object->repo = repo;
-
- /* Parse raw object data */
- def = &git_objects_table[odb_obj->cached.type];
- assert(def->free && def->parse);
-
- if ((error = def->parse(object, odb_obj)) < 0)
- def->free(object);
- else
- *object_out = git_cache_store_parsed(&repo->objects, object);
-
- return error;
-}
-
-void git_object__free(void *obj)
-{
- git_otype type = ((git_object *)obj)->cached.type;
-
- if (type < 0 || ((size_t)type) >= ARRAY_SIZE(git_objects_table) ||
- !git_objects_table[type].free)
- git__free(obj);
- else
- git_objects_table[type].free(obj);
-}
-
-int git_object_lookup_prefix(
- git_object **object_out,
- git_repository *repo,
- const git_oid *id,
- size_t len,
- git_otype type)
-{
- git_object *object = NULL;
- git_odb *odb = NULL;
- git_odb_object *odb_obj = NULL;
- int error = 0;
-
- assert(repo && object_out && id);
-
- if (len < GIT_OID_MINPREFIXLEN) {
- giterr_set(GITERR_OBJECT, "Ambiguous lookup - OID prefix is too short");
- return GIT_EAMBIGUOUS;
- }
-
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < 0)
- return error;
-
- if (len > GIT_OID_HEXSZ)
- len = GIT_OID_HEXSZ;
-
- if (len == GIT_OID_HEXSZ) {
- git_cached_obj *cached = NULL;
-
- /* We want to match the full id : we can first look up in the cache,
- * since there is no need to check for non ambiguousity
- */
- cached = git_cache_get_any(&repo->objects, id);
- if (cached != NULL) {
- if (cached->flags == GIT_CACHE_STORE_PARSED) {
- object = (git_object *)cached;
-
- if (type != GIT_OBJ_ANY && type != object->cached.type) {
- git_object_free(object);
- giterr_set(GITERR_INVALID,
- "The requested type does not match the type in ODB");
- return GIT_ENOTFOUND;
- }
-
- *object_out = object;
- return 0;
- } else if (cached->flags == GIT_CACHE_STORE_RAW) {
- odb_obj = (git_odb_object *)cached;
- } else {
- assert(!"Wrong caching type in the global object cache");
- }
- } else {
- /* Object was not found in the cache, let's explore the backends.
- * We could just use git_odb_read_unique_short_oid,
- * it is the same cost for packed and loose object backends,
- * but it may be much more costly for sqlite and hiredis.
- */
- error = git_odb_read(&odb_obj, odb, id);
- }
- } else {
- git_oid short_oid = {{ 0 }};
-
- git_oid__cpy_prefix(&short_oid, id, len);
-
- /* If len < GIT_OID_HEXSZ (a strict short oid was given), we have
- * 2 options :
- * - We always search in the cache first. If we find that short oid is
- * ambiguous, we can stop. But in all the other cases, we must then
- * explore all the backends (to find an object if there was match,
- * or to check that oid is not ambiguous if we have found 1 match in
- * the cache)
- * - We never explore the cache, go right to exploring the backends
- * We chose the latter : we explore directly the backends.
- */
- error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len);
- }
-
- if (error < 0)
- return error;
-
- error = git_object__from_odb_object(object_out, repo, odb_obj, type);
-
- git_odb_object_free(odb_obj);
-
- return error;
-}
-
-int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {
- return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type);
-}
-
-void git_object_free(git_object *object)
-{
- if (object == NULL)
- return;
-
- git_cached_obj_decref(object);
-}
-
-const git_oid *git_object_id(const git_object *obj)
-{
- assert(obj);
- return &obj->cached.oid;
-}
-
-git_otype git_object_type(const git_object *obj)
-{
- assert(obj);
- return obj->cached.type;
-}
-
-git_repository *git_object_owner(const git_object *obj)
-{
- assert(obj);
- return obj->repo;
-}
-
-const char *git_object_type2string(git_otype type)
-{
- if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
- return "";
-
- return git_objects_table[type].str;
-}
-
-git_otype git_object_string2type(const char *str)
-{
- size_t i;
-
- if (!str || !*str)
- return GIT_OBJ_BAD;
-
- for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
- if (!strcmp(str, git_objects_table[i].str))
- return (git_otype)i;
-
- return GIT_OBJ_BAD;
-}
-
-int git_object_typeisloose(git_otype type)
-{
- if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
- return 0;
-
- return (git_objects_table[type].size > 0) ? 1 : 0;
-}
-
-size_t git_object__size(git_otype type)
-{
- if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
- return 0;
-
- return git_objects_table[type].size;
-}
-
-static int dereference_object(git_object **dereferenced, git_object *obj)
-{
- git_otype type = git_object_type(obj);
-
- switch (type) {
- case GIT_OBJ_COMMIT:
- return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj);
-
- case GIT_OBJ_TAG:
- return git_tag_target(dereferenced, (git_tag*)obj);
-
- case GIT_OBJ_BLOB:
- case GIT_OBJ_TREE:
- return GIT_EPEEL;
-
- default:
- return GIT_EINVALIDSPEC;
- }
-}
-
-static int peel_error(int error, const git_oid *oid, git_otype type)
-{
- const char *type_name;
- char hex_oid[GIT_OID_HEXSZ + 1];
-
- type_name = git_object_type2string(type);
-
- git_oid_fmt(hex_oid, oid);
- hex_oid[GIT_OID_HEXSZ] = '\0';
-
- giterr_set(GITERR_OBJECT, "The git_object of id '%s' can not be "
- "successfully peeled into a %s (git_otype=%i).", hex_oid, type_name, type);
-
- return error;
-}
-
-static int check_type_combination(git_otype type, git_otype target)
-{
- if (type == target)
- return 0;
-
- switch (type) {
- case GIT_OBJ_BLOB:
- case GIT_OBJ_TREE:
- /* a blob or tree can never be peeled to anything but themselves */
- return GIT_EINVALIDSPEC;
- break;
- case GIT_OBJ_COMMIT:
- /* a commit can only be peeled to a tree */
- if (target != GIT_OBJ_TREE && target != GIT_OBJ_ANY)
- return GIT_EINVALIDSPEC;
- break;
- case GIT_OBJ_TAG:
- /* a tag may point to anything, so we let anything through */
- break;
- default:
- return GIT_EINVALIDSPEC;
- }
-
- return 0;
-}
-
-int git_object_peel(
- git_object **peeled,
- const git_object *object,
- git_otype target_type)
-{
- git_object *source, *deref = NULL;
- int error;
-
- assert(object && peeled);
-
- assert(target_type == GIT_OBJ_TAG ||
- target_type == GIT_OBJ_COMMIT ||
- target_type == GIT_OBJ_TREE ||
- target_type == GIT_OBJ_BLOB ||
- target_type == GIT_OBJ_ANY);
-
- if ((error = check_type_combination(git_object_type(object), target_type)) < 0)
- return peel_error(error, git_object_id(object), target_type);
-
- if (git_object_type(object) == target_type)
- return git_object_dup(peeled, (git_object *)object);
-
- source = (git_object *)object;
-
- while (!(error = dereference_object(&deref, source))) {
-
- if (source != object)
- git_object_free(source);
-
- if (git_object_type(deref) == target_type) {
- *peeled = deref;
- return 0;
- }
-
- if (target_type == GIT_OBJ_ANY &&
- git_object_type(deref) != git_object_type(object))
- {
- *peeled = deref;
- return 0;
- }
-
- source = deref;
- deref = NULL;
- }
-
- if (source != object)
- git_object_free(source);
-
- git_object_free(deref);
-
- if (error)
- error = peel_error(error, git_object_id(object), target_type);
-
- return error;
-}
-
-int git_object_dup(git_object **dest, git_object *source)
-{
- git_cached_obj_incref(source);
- *dest = source;
- return 0;
-}
-
-int git_object_lookup_bypath(
- git_object **out,
- const git_object *treeish,
- const char *path,
- git_otype type)
-{
- int error = -1;
- git_tree *tree = NULL;
- git_tree_entry *entry = NULL;
-
- assert(out && treeish && path);
-
- if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJ_TREE)) < 0 ||
- (error = git_tree_entry_bypath(&entry, tree, path)) < 0)
- {
- goto cleanup;
- }
-
- if (type != GIT_OBJ_ANY && git_tree_entry_type(entry) != type)
- {
- giterr_set(GITERR_OBJECT,
- "object at path '%s' is not of the asked-for type %d",
- path, type);
- error = GIT_EINVALIDSPEC;
- goto cleanup;
- }
-
- error = git_tree_entry_to_object(out, git_object_owner(treeish), entry);
-
-cleanup:
- git_tree_entry_free(entry);
- git_tree_free(tree);
- return error;
-}
-
-int git_object_short_id(git_buf *out, const git_object *obj)
-{
- git_repository *repo;
- int len = GIT_ABBREV_DEFAULT, error;
- git_oid id = {{0}};
- git_odb *odb;
-
- assert(out && obj);
-
- git_buf_sanitize(out);
- repo = git_object_owner(obj);
-
- if ((error = git_repository__cvar(&len, repo, GIT_CVAR_ABBREV)) < 0)
- return error;
-
- if ((error = git_repository_odb(&odb, repo)) < 0)
- return error;
-
- while (len < GIT_OID_HEXSZ) {
- /* set up short oid */
- memcpy(&id.id, &obj->cached.oid.id, (len + 1) / 2);
- if (len & 1)
- id.id[len / 2] &= 0xf0;
-
- error = git_odb_exists_prefix(NULL, odb, &id, len);
- if (error != GIT_EAMBIGUOUS)
- break;
-
- giterr_clear();
- len++;
- }
-
- if (!error && !(error = git_buf_grow(out, len + 1))) {
- git_oid_tostr(out->ptr, len + 1, &id);
- out->size = len;
- }
-
- git_odb_free(odb);
-
- return error;
-}
-
-bool git_object__is_valid(
- git_repository *repo, const git_oid *id, git_otype expected_type)
-{
- git_odb *odb;
- git_otype actual_type;
- size_t len;
- int error;
-
- if (!git_object__strict_input_validation)
- return true;
-
- if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
- (error = git_odb_read_header(&len, &actual_type, odb, id)) < 0)
- return false;
-
- if (expected_type != GIT_OBJ_ANY && expected_type != actual_type) {
- giterr_set(GITERR_INVALID,
- "the requested type does not match the type in the ODB");
- return false;
- }
-
- return true;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_object_h__
-#define INCLUDE_object_h__
-
-#include "repository.h"
-
-extern bool git_object__strict_input_validation;
-
-/** Base git object for inheritance */
-struct git_object {
- git_cached_obj cached;
- git_repository *repo;
-};
-
-/* fully free the object; internal method, DO NOT EXPORT */
-void git_object__free(void *object);
-
-int git_object__from_odb_object(
- git_object **object_out,
- git_repository *repo,
- git_odb_object *odb_obj,
- git_otype type);
-
-int git_object__resolve_to_type(git_object **obj, git_otype type);
-
-int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
-
-void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
-
-bool git_object__is_valid(
- git_repository *repo, const git_oid *id, git_otype expected_type);
-
-GIT_INLINE(git_otype) git_object__type_from_filemode(git_filemode_t mode)
-{
- switch (mode) {
- case GIT_FILEMODE_TREE:
- return GIT_OBJ_TREE;
- case GIT_FILEMODE_COMMIT:
- return GIT_OBJ_COMMIT;
- case GIT_FILEMODE_BLOB:
- case GIT_FILEMODE_BLOB_EXECUTABLE:
- case GIT_FILEMODE_LINK:
- return GIT_OBJ_BLOB;
- default:
- return GIT_OBJ_BAD;
- }
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "git2/object.h"
-
-#include "common.h"
-#include "repository.h"
-
-#include "commit.h"
-#include "tree.h"
-#include "blob.h"
-#include "tag.h"
-
-/**
- * Commit
- */
-int git_commit_lookup(git_commit **out, git_repository *repo, const git_oid *id)
-{
- return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_COMMIT);
-}
-
-int git_commit_lookup_prefix(git_commit **out, git_repository *repo, const git_oid *id, size_t len)
-{
- return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_COMMIT);
-}
-
-void git_commit_free(git_commit *obj)
-{
- git_object_free((git_object *)obj);
-}
-
-const git_oid *git_commit_id(const git_commit *obj)
-{
- return git_object_id((const git_object *)obj);
-}
-
-git_repository *git_commit_owner(const git_commit *obj)
-{
- return git_object_owner((const git_object *)obj);
-}
-
-int git_commit_dup(git_commit **out, git_commit *obj)
-{
- return git_object_dup((git_object **)out, (git_object *)obj);
-}
-
-/**
- * Tree
- */
-int git_tree_lookup(git_tree **out, git_repository *repo, const git_oid *id)
-{
- return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TREE);
-}
-
-int git_tree_lookup_prefix(git_tree **out, git_repository *repo, const git_oid *id, size_t len)
-{
- return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_TREE);
-}
-
-void git_tree_free(git_tree *obj)
-{
- git_object_free((git_object *)obj);
-}
-
-const git_oid *git_tree_id(const git_tree *obj)
-{
- return git_object_id((const git_object *)obj);
-}
-
-git_repository *git_tree_owner(const git_tree *obj)
-{
- return git_object_owner((const git_object *)obj);
-}
-
-int git_tree_dup(git_tree **out, git_tree *obj)
-{
- return git_object_dup((git_object **)out, (git_object *)obj);
-}
-
-/**
- * Tag
- */
-int git_tag_lookup(git_tag **out, git_repository *repo, const git_oid *id)
-{
- return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TAG);
-}
-
-int git_tag_lookup_prefix(git_tag **out, git_repository *repo, const git_oid *id, size_t len)
-{
- return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_TAG);
-}
-
-void git_tag_free(git_tag *obj)
-{
- git_object_free((git_object *)obj);
-}
-
-const git_oid *git_tag_id(const git_tag *obj)
-{
- return git_object_id((const git_object *)obj);
-}
-
-git_repository *git_tag_owner(const git_tag *obj)
-{
- return git_object_owner((const git_object *)obj);
-}
-
-int git_tag_dup(git_tag **out, git_tag *obj)
-{
- return git_object_dup((git_object **)out, (git_object *)obj);
-}
-
-/**
- * Blob
- */
-int git_blob_lookup(git_blob **out, git_repository *repo, const git_oid *id)
-{
- return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_BLOB);
-}
-
-int git_blob_lookup_prefix(git_blob **out, git_repository *repo, const git_oid *id, size_t len)
-{
- return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_BLOB);
-}
-
-void git_blob_free(git_blob *obj)
-{
- git_object_free((git_object *)obj);
-}
-
-const git_oid *git_blob_id(const git_blob *obj)
-{
- return git_object_id((const git_object *)obj);
-}
-
-git_repository *git_blob_owner(const git_blob *obj)
-{
- return git_object_owner((const git_object *)obj);
-}
-
-int git_blob_dup(git_blob **out, git_blob *obj)
-{
- return git_object_dup((git_object **)out, (git_object *)obj);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include <zlib.h>
-#include "git2/object.h"
-#include "git2/sys/odb_backend.h"
-#include "fileops.h"
-#include "hash.h"
-#include "odb.h"
-#include "delta.h"
-#include "filter.h"
-#include "repository.h"
-
-#include "git2/odb_backend.h"
-#include "git2/oid.h"
-#include "git2/oidarray.h"
-
-#define GIT_ALTERNATES_FILE "info/alternates"
-
-/*
- * We work under the assumption that most objects for long-running
- * operations will be packed
- */
-#define GIT_LOOSE_PRIORITY 1
-#define GIT_PACKED_PRIORITY 2
-
-#define GIT_ALTERNATES_MAX_DEPTH 5
-
-typedef struct
-{
- git_odb_backend *backend;
- int priority;
- bool is_alternate;
- ino_t disk_inode;
-} backend_internal;
-
-static git_cache *odb_cache(git_odb *odb)
-{
- if (odb->rc.owner != NULL) {
- git_repository *owner = odb->rc.owner;
- return &owner->objects;
- }
-
- return &odb->own_cache;
-}
-
-static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id);
-static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
-
-static git_otype odb_hardcoded_type(const git_oid *id)
-{
- static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
- 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }};
-
- if (!git_oid_cmp(id, &empty_tree))
- return GIT_OBJ_TREE;
-
- return GIT_OBJ_BAD;
-}
-
-static int odb_read_hardcoded(git_rawobj *raw, const git_oid *id)
-{
- git_otype type = odb_hardcoded_type(id);
- if (type == GIT_OBJ_BAD)
- return -1;
-
- raw->type = type;
- raw->len = 0;
- raw->data = git__calloc(1, sizeof(uint8_t));
- return 0;
-}
-
-int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type)
-{
- const char *type_str = git_object_type2string(obj_type);
- int len = p_snprintf(hdr, n, "%s %lld", type_str, (long long)obj_len);
- assert(len > 0 && len <= (int)n);
- return len+1;
-}
-
-int git_odb__hashobj(git_oid *id, git_rawobj *obj)
-{
- git_buf_vec vec[2];
- char header[64];
- int hdrlen;
-
- assert(id && obj);
-
- if (!git_object_typeisloose(obj->type))
- return -1;
-
- if (!obj->data && obj->len != 0)
- return -1;
-
- hdrlen = git_odb__format_object_header(header, sizeof(header), obj->len, obj->type);
-
- vec[0].data = header;
- vec[0].len = hdrlen;
- vec[1].data = obj->data;
- vec[1].len = obj->len;
-
- git_hash_vec(id, vec, 2);
-
- return 0;
-}
-
-
-static git_odb_object *odb_object__alloc(const git_oid *oid, git_rawobj *source)
-{
- git_odb_object *object = git__calloc(1, sizeof(git_odb_object));
-
- if (object != NULL) {
- git_oid_cpy(&object->cached.oid, oid);
- object->cached.type = source->type;
- object->cached.size = source->len;
- object->buffer = source->data;
- }
-
- return object;
-}
-
-void git_odb_object__free(void *object)
-{
- if (object != NULL) {
- git__free(((git_odb_object *)object)->buffer);
- git__free(object);
- }
-}
-
-const git_oid *git_odb_object_id(git_odb_object *object)
-{
- return &object->cached.oid;
-}
-
-const void *git_odb_object_data(git_odb_object *object)
-{
- return object->buffer;
-}
-
-size_t git_odb_object_size(git_odb_object *object)
-{
- return object->cached.size;
-}
-
-git_otype git_odb_object_type(git_odb_object *object)
-{
- return object->cached.type;
-}
-
-int git_odb_object_dup(git_odb_object **dest, git_odb_object *source)
-{
- git_cached_obj_incref(source);
- *dest = source;
- return 0;
-}
-
-void git_odb_object_free(git_odb_object *object)
-{
- if (object == NULL)
- return;
-
- git_cached_obj_decref(object);
-}
-
-int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
-{
- int hdr_len;
- char hdr[64], buffer[FILEIO_BUFSIZE];
- git_hash_ctx ctx;
- ssize_t read_len = 0;
- int error = 0;
-
- if (!git_object_typeisloose(type)) {
- giterr_set(GITERR_INVALID, "Invalid object type for hash");
- return -1;
- }
-
- if ((error = git_hash_ctx_init(&ctx)) < 0)
- return -1;
-
- hdr_len = git_odb__format_object_header(hdr, sizeof(hdr), size, type);
-
- if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0)
- goto done;
-
- while (size > 0 && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
- if ((error = git_hash_update(&ctx, buffer, read_len)) < 0)
- goto done;
-
- size -= read_len;
- }
-
- /* If p_read returned an error code, the read obviously failed.
- * If size is not zero, the file was truncated after we originally
- * stat'd it, so we consider this a read failure too */
- if (read_len < 0 || size > 0) {
- giterr_set(GITERR_OS, "Error reading file for hashing");
- error = -1;
-
- goto done;
- }
-
- error = git_hash_final(out, &ctx);
-
-done:
- git_hash_ctx_cleanup(&ctx);
- return error;
-}
-
-int git_odb__hashfd_filtered(
- git_oid *out, git_file fd, size_t size, git_otype type, git_filter_list *fl)
-{
- int error;
- git_buf raw = GIT_BUF_INIT;
-
- if (!fl)
- return git_odb__hashfd(out, fd, size, type);
-
- /* size of data is used in header, so we have to read the whole file
- * into memory to apply filters before beginning to calculate the hash
- */
-
- if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) {
- git_buf post = GIT_BUF_INIT;
-
- error = git_filter_list_apply_to_data(&post, fl, &raw);
-
- git_buf_free(&raw);
-
- if (!error)
- error = git_odb_hash(out, post.ptr, post.size, type);
-
- git_buf_free(&post);
- }
-
- return error;
-}
-
-int git_odb__hashlink(git_oid *out, const char *path)
-{
- struct stat st;
- int size;
- int result;
-
- if (git_path_lstat(path, &st) < 0)
- return -1;
-
- if (!git__is_int(st.st_size) || (int)st.st_size < 0) {
- giterr_set(GITERR_FILESYSTEM, "File size overflow for 32-bit systems");
- return -1;
- }
-
- size = (int)st.st_size;
-
- if (S_ISLNK(st.st_mode)) {
- char *link_data;
- int read_len;
- size_t alloc_size;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_size, size, 1);
- link_data = git__malloc(alloc_size);
- GITERR_CHECK_ALLOC(link_data);
-
- read_len = p_readlink(path, link_data, size);
- link_data[size] = '\0';
- if (read_len != size) {
- giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
- git__free(link_data);
- return -1;
- }
-
- result = git_odb_hash(out, link_data, size, GIT_OBJ_BLOB);
- git__free(link_data);
- } else {
- int fd = git_futils_open_ro(path);
- if (fd < 0)
- return -1;
- result = git_odb__hashfd(out, fd, size, GIT_OBJ_BLOB);
- p_close(fd);
- }
-
- return result;
-}
-
-int git_odb_hashfile(git_oid *out, const char *path, git_otype type)
-{
- git_off_t size;
- int result, fd = git_futils_open_ro(path);
- if (fd < 0)
- return fd;
-
- if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) {
- giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
- p_close(fd);
- return -1;
- }
-
- result = git_odb__hashfd(out, fd, (size_t)size, type);
- p_close(fd);
- return result;
-}
-
-int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type)
-{
- git_rawobj raw;
-
- assert(id);
-
- raw.data = (void *)data;
- raw.len = len;
- raw.type = type;
-
- return git_odb__hashobj(id, &raw);
-}
-
-/**
- * FAKE WSTREAM
- */
-
-typedef struct {
- git_odb_stream stream;
- char *buffer;
- size_t size, written;
- git_otype type;
-} fake_wstream;
-
-static int fake_wstream__fwrite(git_odb_stream *_stream, const git_oid *oid)
-{
- fake_wstream *stream = (fake_wstream *)_stream;
- return _stream->backend->write(_stream->backend, oid, stream->buffer, stream->size, stream->type);
-}
-
-static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len)
-{
- fake_wstream *stream = (fake_wstream *)_stream;
-
- if (stream->written + len > stream->size)
- return -1;
-
- memcpy(stream->buffer + stream->written, data, len);
- stream->written += len;
- return 0;
-}
-
-static void fake_wstream__free(git_odb_stream *_stream)
-{
- fake_wstream *stream = (fake_wstream *)_stream;
-
- git__free(stream->buffer);
- git__free(stream);
-}
-
-static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, git_off_t size, git_otype type)
-{
- fake_wstream *stream;
-
- if (!git__is_ssizet(size)) {
- giterr_set(GITERR_ODB, "object size too large to keep in memory");
- return -1;
- }
-
- stream = git__calloc(1, sizeof(fake_wstream));
- GITERR_CHECK_ALLOC(stream);
-
- stream->size = size;
- stream->type = type;
- stream->buffer = git__malloc(size);
- if (stream->buffer == NULL) {
- git__free(stream);
- return -1;
- }
-
- stream->stream.backend = backend;
- stream->stream.read = NULL; /* read only */
- stream->stream.write = &fake_wstream__write;
- stream->stream.finalize_write = &fake_wstream__fwrite;
- stream->stream.free = &fake_wstream__free;
- stream->stream.mode = GIT_STREAM_WRONLY;
-
- *stream_p = (git_odb_stream *)stream;
- return 0;
-}
-
-/***********************************************************
- *
- * OBJECT DATABASE PUBLIC API
- *
- * Public calls for the ODB functionality
- *
- ***********************************************************/
-
-static int backend_sort_cmp(const void *a, const void *b)
-{
- const backend_internal *backend_a = (const backend_internal *)(a);
- const backend_internal *backend_b = (const backend_internal *)(b);
-
- if (backend_b->priority == backend_a->priority) {
- if (backend_a->is_alternate)
- return -1;
- if (backend_b->is_alternate)
- return 1;
- return 0;
- }
- return (backend_b->priority - backend_a->priority);
-}
-
-int git_odb_new(git_odb **out)
-{
- git_odb *db = git__calloc(1, sizeof(*db));
- GITERR_CHECK_ALLOC(db);
-
- if (git_cache_init(&db->own_cache) < 0 ||
- git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) {
- git__free(db);
- return -1;
- }
-
- *out = db;
- GIT_REFCOUNT_INC(db);
- return 0;
-}
-
-static int add_backend_internal(
- git_odb *odb, git_odb_backend *backend,
- int priority, bool is_alternate, ino_t disk_inode)
-{
- backend_internal *internal;
-
- assert(odb && backend);
-
- GITERR_CHECK_VERSION(backend, GIT_ODB_BACKEND_VERSION, "git_odb_backend");
-
- /* Check if the backend is already owned by another ODB */
- assert(!backend->odb || backend->odb == odb);
-
- internal = git__malloc(sizeof(backend_internal));
- GITERR_CHECK_ALLOC(internal);
-
- internal->backend = backend;
- internal->priority = priority;
- internal->is_alternate = is_alternate;
- internal->disk_inode = disk_inode;
-
- if (git_vector_insert(&odb->backends, internal) < 0) {
- git__free(internal);
- return -1;
- }
-
- git_vector_sort(&odb->backends);
- internal->backend->odb = odb;
- return 0;
-}
-
-int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority)
-{
- return add_backend_internal(odb, backend, priority, false, 0);
-}
-
-int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)
-{
- return add_backend_internal(odb, backend, priority, true, 0);
-}
-
-size_t git_odb_num_backends(git_odb *odb)
-{
- assert(odb);
- return odb->backends.length;
-}
-
-static int git_odb__error_unsupported_in_backend(const char *action)
-{
- giterr_set(GITERR_ODB,
- "Cannot %s - unsupported in the loaded odb backends", action);
- return -1;
-}
-
-
-int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos)
-{
- backend_internal *internal;
-
- assert(out && odb);
- internal = git_vector_get(&odb->backends, pos);
-
- if (internal && internal->backend) {
- *out = internal->backend;
- return 0;
- }
-
- giterr_set(GITERR_ODB, "No ODB backend loaded at index %" PRIuZ, pos);
- return GIT_ENOTFOUND;
-}
-
-static int add_default_backends(
- git_odb *db, const char *objects_dir,
- bool as_alternates, int alternate_depth)
-{
- size_t i;
- struct stat st;
- ino_t inode;
- git_odb_backend *loose, *packed;
-
- /* TODO: inodes are not really relevant on Win32, so we need to find
- * a cross-platform workaround for this */
-#ifdef GIT_WIN32
- GIT_UNUSED(i);
- GIT_UNUSED(st);
-
- inode = 0;
-#else
- if (p_stat(objects_dir, &st) < 0) {
- if (as_alternates)
- return 0;
-
- giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir);
- return -1;
- }
-
- inode = st.st_ino;
-
- for (i = 0; i < db->backends.length; ++i) {
- backend_internal *backend = git_vector_get(&db->backends, i);
- if (backend->disk_inode == inode)
- return 0;
- }
-#endif
-
- /* add the loose object backend */
- if (git_odb_backend_loose(&loose, objects_dir, -1, 0, 0, 0) < 0 ||
- add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0)
- return -1;
-
- /* add the packed file backend */
- if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
- add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates, inode) < 0)
- return -1;
-
- return load_alternates(db, objects_dir, alternate_depth);
-}
-
-static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth)
-{
- git_buf alternates_path = GIT_BUF_INIT;
- git_buf alternates_buf = GIT_BUF_INIT;
- char *buffer;
- const char *alternate;
- int result = 0;
-
- /* Git reports an error, we just ignore anything deeper */
- if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH)
- return 0;
-
- if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0)
- return -1;
-
- if (git_path_exists(alternates_path.ptr) == false) {
- git_buf_free(&alternates_path);
- return 0;
- }
-
- if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < 0) {
- git_buf_free(&alternates_path);
- return -1;
- }
-
- buffer = (char *)alternates_buf.ptr;
-
- /* add each alternate as a new backend; one alternate per line */
- while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) {
- if (*alternate == '\0' || *alternate == '#')
- continue;
-
- /*
- * Relative path: build based on the current `objects`
- * folder. However, relative paths are only allowed in
- * the current repository.
- */
- if (*alternate == '.' && !alternate_depth) {
- if ((result = git_buf_joinpath(&alternates_path, objects_dir, alternate)) < 0)
- break;
- alternate = git_buf_cstr(&alternates_path);
- }
-
- if ((result = add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0)
- break;
- }
-
- git_buf_free(&alternates_path);
- git_buf_free(&alternates_buf);
-
- return result;
-}
-
-int git_odb_add_disk_alternate(git_odb *odb, const char *path)
-{
- return add_default_backends(odb, path, true, 0);
-}
-
-int git_odb_open(git_odb **out, const char *objects_dir)
-{
- git_odb *db;
-
- assert(out && objects_dir);
-
- *out = NULL;
-
- if (git_odb_new(&db) < 0)
- return -1;
-
- if (add_default_backends(db, objects_dir, 0, 0) < 0) {
- git_odb_free(db);
- return -1;
- }
-
- *out = db;
- return 0;
-}
-
-static void odb_free(git_odb *db)
-{
- size_t i;
-
- for (i = 0; i < db->backends.length; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *backend = internal->backend;
-
- backend->free(backend);
-
- git__free(internal);
- }
-
- git_vector_free(&db->backends);
- git_cache_free(&db->own_cache);
-
- git__memzero(db, sizeof(*db));
- git__free(db);
-}
-
-void git_odb_free(git_odb *db)
-{
- if (db == NULL)
- return;
-
- GIT_REFCOUNT_DEC(db, odb_free);
-}
-
-static int odb_exists_1(
- git_odb *db,
- const git_oid *id,
- bool only_refreshed)
-{
- size_t i;
- bool found = false;
-
- for (i = 0; i < db->backends.length && !found; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- if (only_refreshed && !b->refresh)
- continue;
-
- if (b->exists != NULL)
- found = (bool)b->exists(b, id);
- }
-
- return (int)found;
-}
-
-static int odb_freshen_1(
- git_odb *db,
- const git_oid *id,
- bool only_refreshed)
-{
- size_t i;
- bool found = false;
-
- for (i = 0; i < db->backends.length && !found; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- if (only_refreshed && !b->refresh)
- continue;
-
- if (b->freshen != NULL)
- found = !b->freshen(b, id);
- else if (b->exists != NULL)
- found = b->exists(b, id);
- }
-
- return (int)found;
-}
-
-static int odb_freshen(git_odb *db, const git_oid *id)
-{
- assert(db && id);
-
- if (odb_freshen_1(db, id, false))
- return 1;
-
- if (!git_odb_refresh(db))
- return odb_freshen_1(db, id, true);
-
- /* Failed to refresh, hence not found */
- return 0;
-}
-
-int git_odb_exists(git_odb *db, const git_oid *id)
-{
- git_odb_object *object;
-
- assert(db && id);
-
- if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
- git_odb_object_free(object);
- return 1;
- }
-
- if (odb_exists_1(db, id, false))
- return 1;
-
- if (!git_odb_refresh(db))
- return odb_exists_1(db, id, true);
-
- /* Failed to refresh, hence not found */
- return 0;
-}
-
-static int odb_exists_prefix_1(git_oid *out, git_odb *db,
- const git_oid *key, size_t len, bool only_refreshed)
-{
- size_t i;
- int error = GIT_ENOTFOUND, num_found = 0;
- git_oid last_found = {{0}}, found;
-
- for (i = 0; i < db->backends.length; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- if (only_refreshed && !b->refresh)
- continue;
-
- if (!b->exists_prefix)
- continue;
-
- error = b->exists_prefix(&found, b, key, len);
- if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
- continue;
- if (error)
- return error;
-
- /* make sure found item doesn't introduce ambiguity */
- if (num_found) {
- if (git_oid__cmp(&last_found, &found))
- return git_odb__error_ambiguous("multiple matches for prefix");
- } else {
- git_oid_cpy(&last_found, &found);
- num_found++;
- }
- }
-
- if (!num_found)
- return GIT_ENOTFOUND;
-
- if (out)
- git_oid_cpy(out, &last_found);
-
- return 0;
-}
-
-int git_odb_exists_prefix(
- git_oid *out, git_odb *db, const git_oid *short_id, size_t len)
-{
- int error;
- git_oid key = {{0}};
-
- assert(db && short_id);
-
- if (len < GIT_OID_MINPREFIXLEN)
- return git_odb__error_ambiguous("prefix length too short");
-
- if (len >= GIT_OID_HEXSZ) {
- if (git_odb_exists(db, short_id)) {
- if (out)
- git_oid_cpy(out, short_id);
- return 0;
- } else {
- return git_odb__error_notfound(
- "no match for id prefix", short_id, len);
- }
- }
-
- git_oid__cpy_prefix(&key, short_id, len);
-
- error = odb_exists_prefix_1(out, db, &key, len, false);
-
- if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
- error = odb_exists_prefix_1(out, db, &key, len, true);
-
- if (error == GIT_ENOTFOUND)
- return git_odb__error_notfound("no match for id prefix", &key, len);
-
- return error;
-}
-
-int git_odb_expand_ids(
- git_odb *db,
- git_odb_expand_id *ids,
- size_t count)
-{
- size_t i;
-
- assert(db && ids);
-
- for (i = 0; i < count; i++) {
- git_odb_expand_id *query = &ids[i];
- int error = GIT_EAMBIGUOUS;
-
- if (!query->type)
- query->type = GIT_OBJ_ANY;
-
- /* if we have a short OID, expand it first */
- if (query->length >= GIT_OID_MINPREFIXLEN && query->length < GIT_OID_HEXSZ) {
- git_oid actual_id;
-
- error = odb_exists_prefix_1(&actual_id, db, &query->id, query->length, false);
- if (!error) {
- git_oid_cpy(&query->id, &actual_id);
- query->length = GIT_OID_HEXSZ;
- }
- }
-
- /*
- * now we ought to have a 40-char OID, either because we've expanded it
- * or because the user passed a full OID. Ensure its type is right.
- */
- if (query->length >= GIT_OID_HEXSZ) {
- git_otype actual_type;
-
- error = odb_otype_fast(&actual_type, db, &query->id);
- if (!error) {
- if (query->type != GIT_OBJ_ANY && query->type != actual_type)
- error = GIT_ENOTFOUND;
- else
- query->type = actual_type;
- }
- }
-
- switch (error) {
- /* no errors, so we've successfully expanded the OID */
- case 0:
- continue;
-
- /* the object is missing or ambiguous */
- case GIT_ENOTFOUND:
- case GIT_EAMBIGUOUS:
- memset(&query->id, 0, sizeof(git_oid));
- query->length = 0;
- query->type = 0;
- break;
-
- /* something went very wrong with the ODB; bail hard */
- default:
- return error;
- }
- }
-
- giterr_clear();
- return 0;
-}
-
-int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
-{
- int error;
- git_odb_object *object;
-
- error = git_odb__read_header_or_object(&object, len_p, type_p, db, id);
-
- if (object)
- git_odb_object_free(object);
-
- return error;
-}
-
-static int odb_read_header_1(
- size_t *len_p, git_otype *type_p, git_odb *db,
- const git_oid *id, bool only_refreshed)
-{
- size_t i;
- git_otype ht;
- bool passthrough = false;
- int error;
-
- if (!only_refreshed && (ht = odb_hardcoded_type(id)) != GIT_OBJ_BAD) {
- *type_p = ht;
- *len_p = 0;
- return 0;
- }
-
- for (i = 0; i < db->backends.length; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- if (only_refreshed && !b->refresh)
- continue;
-
- if (!b->read_header) {
- passthrough = true;
- continue;
- }
-
- error = b->read_header(len_p, type_p, b, id);
-
- switch (error) {
- case GIT_PASSTHROUGH:
- passthrough = true;
- break;
- case GIT_ENOTFOUND:
- break;
- default:
- return error;
- }
- }
-
- return passthrough ? GIT_PASSTHROUGH : GIT_ENOTFOUND;
-}
-
-int git_odb__read_header_or_object(
- git_odb_object **out, size_t *len_p, git_otype *type_p,
- git_odb *db, const git_oid *id)
-{
- int error = GIT_ENOTFOUND;
- git_odb_object *object;
-
- assert(db && id && out && len_p && type_p);
-
- if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
- *len_p = object->cached.size;
- *type_p = object->cached.type;
- *out = object;
- return 0;
- }
-
- *out = NULL;
- error = odb_read_header_1(len_p, type_p, db, id, false);
-
- if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
- error = odb_read_header_1(len_p, type_p, db, id, true);
-
- if (error == GIT_ENOTFOUND)
- return git_odb__error_notfound("cannot read header for", id, GIT_OID_HEXSZ);
-
- /* we found the header; return early */
- if (!error)
- return 0;
-
- if (error == GIT_PASSTHROUGH) {
- /*
- * no backend has header-reading functionality
- * so try using `git_odb_read` instead
- */
- error = git_odb_read(&object, db, id);
- if (!error) {
- *len_p = object->cached.size;
- *type_p = object->cached.type;
- *out = object;
- }
- }
-
- return error;
-}
-
-static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
- bool only_refreshed)
-{
- size_t i;
- git_rawobj raw;
- git_odb_object *object;
- bool found = false;
-
- if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0)
- found = true;
-
- for (i = 0; i < db->backends.length && !found; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- if (only_refreshed && !b->refresh)
- continue;
-
- if (b->read != NULL) {
- int error = b->read(&raw.data, &raw.len, &raw.type, b, id);
- if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND)
- continue;
-
- if (error < 0)
- return error;
-
- found = true;
- }
- }
-
- if (!found)
- return GIT_ENOTFOUND;
-
- giterr_clear();
- if ((object = odb_object__alloc(id, &raw)) == NULL)
- return -1;
-
- *out = git_cache_store_raw(odb_cache(db), object);
- return 0;
-}
-
-int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
-{
- int error;
-
- assert(out && db && id);
-
- *out = git_cache_get_raw(odb_cache(db), id);
- if (*out != NULL)
- return 0;
-
- error = odb_read_1(out, db, id, false);
-
- if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
- error = odb_read_1(out, db, id, true);
-
- if (error == GIT_ENOTFOUND)
- return git_odb__error_notfound("no match for id", id, GIT_OID_HEXSZ);
-
- return error;
-}
-
-static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id)
-{
- git_odb_object *object;
- size_t _unused;
- int error;
-
- if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
- *type_p = object->cached.type;
- return 0;
- }
-
- error = odb_read_header_1(&_unused, type_p, db, id, false);
-
- if (error == GIT_PASSTHROUGH) {
- error = odb_read_1(&object, db, id, false);
- if (!error)
- *type_p = object->cached.type;
- git_odb_object_free(object);
- }
-
- return error;
-}
-
-static int read_prefix_1(git_odb_object **out, git_odb *db,
- const git_oid *key, size_t len, bool only_refreshed)
-{
- size_t i;
- int error = GIT_ENOTFOUND;
- git_oid found_full_oid = {{0}};
- git_rawobj raw;
- void *data = NULL;
- bool found = false;
- git_odb_object *object;
-
- for (i = 0; i < db->backends.length; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- if (only_refreshed && !b->refresh)
- continue;
-
- if (b->read_prefix != NULL) {
- git_oid full_oid;
- error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len);
- if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
- continue;
-
- if (error)
- return error;
-
- git__free(data);
- data = raw.data;
-
- if (found && git_oid__cmp(&full_oid, &found_full_oid)) {
- git__free(raw.data);
- return git_odb__error_ambiguous("multiple matches for prefix");
- }
-
- found_full_oid = full_oid;
- found = true;
- }
- }
-
- if (!found)
- return GIT_ENOTFOUND;
-
- if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
- return -1;
-
- *out = git_cache_store_raw(odb_cache(db), object);
- return 0;
-}
-
-int git_odb_read_prefix(
- git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len)
-{
- git_oid key = {{0}};
- int error;
-
- assert(out && db);
-
- if (len < GIT_OID_MINPREFIXLEN)
- return git_odb__error_ambiguous("prefix length too short");
-
- if (len > GIT_OID_HEXSZ)
- len = GIT_OID_HEXSZ;
-
- if (len == GIT_OID_HEXSZ) {
- *out = git_cache_get_raw(odb_cache(db), short_id);
- if (*out != NULL)
- return 0;
- }
-
- git_oid__cpy_prefix(&key, short_id, len);
-
- error = read_prefix_1(out, db, &key, len, false);
-
- if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
- error = read_prefix_1(out, db, &key, len, true);
-
- if (error == GIT_ENOTFOUND)
- return git_odb__error_notfound("no match for prefix", &key, len);
-
- return error;
-}
-
-int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload)
-{
- unsigned int i;
- backend_internal *internal;
-
- git_vector_foreach(&db->backends, i, internal) {
- git_odb_backend *b = internal->backend;
- int error = b->foreach(b, cb, payload);
- if (error < 0)
- return error;
- }
-
- return 0;
-}
-
-int git_odb_write(
- git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
-{
- size_t i;
- int error = GIT_ERROR;
- git_odb_stream *stream;
-
- assert(oid && db);
-
- git_odb_hash(oid, data, len, type);
- if (odb_freshen(db, oid))
- return 0;
-
- for (i = 0; i < db->backends.length && error < 0; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- /* we don't write in alternates! */
- if (internal->is_alternate)
- continue;
-
- if (b->write != NULL)
- error = b->write(b, oid, data, len, type);
- }
-
- if (!error || error == GIT_PASSTHROUGH)
- return 0;
-
- /* if no backends were able to write the object directly, we try a
- * streaming write to the backends; just write the whole object into the
- * stream in one push
- */
- if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0)
- return error;
-
- stream->write(stream, data, len);
- error = stream->finalize_write(stream, oid);
- git_odb_stream_free(stream);
-
- return error;
-}
-
-static void hash_header(git_hash_ctx *ctx, git_off_t size, git_otype type)
-{
- char header[64];
- int hdrlen;
-
- hdrlen = git_odb__format_object_header(header, sizeof(header), size, type);
- git_hash_update(ctx, header, hdrlen);
-}
-
-int git_odb_open_wstream(
- git_odb_stream **stream, git_odb *db, git_off_t size, git_otype type)
-{
- size_t i, writes = 0;
- int error = GIT_ERROR;
- git_hash_ctx *ctx = NULL;
-
- assert(stream && db);
-
- for (i = 0; i < db->backends.length && error < 0; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- /* we don't write in alternates! */
- if (internal->is_alternate)
- continue;
-
- if (b->writestream != NULL) {
- ++writes;
- error = b->writestream(stream, b, size, type);
- } else if (b->write != NULL) {
- ++writes;
- error = init_fake_wstream(stream, b, size, type);
- }
- }
-
- if (error < 0) {
- if (error == GIT_PASSTHROUGH)
- error = 0;
- else if (!writes)
- error = git_odb__error_unsupported_in_backend("write object");
-
- goto done;
- }
-
- ctx = git__malloc(sizeof(git_hash_ctx));
- GITERR_CHECK_ALLOC(ctx);
-
- if ((error = git_hash_ctx_init(ctx)) < 0)
- goto done;
-
- hash_header(ctx, size, type);
- (*stream)->hash_ctx = ctx;
-
- (*stream)->declared_size = size;
- (*stream)->received_bytes = 0;
-
-done:
- return error;
-}
-
-static int git_odb_stream__invalid_length(
- const git_odb_stream *stream,
- const char *action)
-{
- giterr_set(GITERR_ODB,
- "Cannot %s - "
- "Invalid length. %"PRIuZ" was expected. The "
- "total size of the received chunks amounts to %"PRIuZ".",
- action, stream->declared_size, stream->received_bytes);
-
- return -1;
-}
-
-int git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len)
-{
- git_hash_update(stream->hash_ctx, buffer, len);
-
- stream->received_bytes += len;
-
- if (stream->received_bytes > stream->declared_size)
- return git_odb_stream__invalid_length(stream,
- "stream_write()");
-
- return stream->write(stream, buffer, len);
-}
-
-int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream)
-{
- if (stream->received_bytes != stream->declared_size)
- return git_odb_stream__invalid_length(stream,
- "stream_finalize_write()");
-
- git_hash_final(out, stream->hash_ctx);
-
- if (odb_freshen(stream->backend->odb, out))
- return 0;
-
- return stream->finalize_write(stream, out);
-}
-
-int git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len)
-{
- return stream->read(stream, buffer, len);
-}
-
-void git_odb_stream_free(git_odb_stream *stream)
-{
- if (stream == NULL)
- return;
-
- git_hash_ctx_cleanup(stream->hash_ctx);
- git__free(stream->hash_ctx);
- stream->free(stream);
-}
-
-int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
-{
- size_t i, reads = 0;
- int error = GIT_ERROR;
-
- assert(stream && db);
-
- for (i = 0; i < db->backends.length && error < 0; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- if (b->readstream != NULL) {
- ++reads;
- error = b->readstream(stream, b, oid);
- }
- }
-
- if (error == GIT_PASSTHROUGH)
- error = 0;
- if (error < 0 && !reads)
- error = git_odb__error_unsupported_in_backend("read object streamed");
-
- return error;
-}
-
-int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer_progress_cb progress_cb, void *progress_payload)
-{
- size_t i, writes = 0;
- int error = GIT_ERROR;
-
- assert(out && db);
-
- for (i = 0; i < db->backends.length && error < 0; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- /* we don't write in alternates! */
- if (internal->is_alternate)
- continue;
-
- if (b->writepack != NULL) {
- ++writes;
- error = b->writepack(out, b, db, progress_cb, progress_payload);
- }
- }
-
- if (error == GIT_PASSTHROUGH)
- error = 0;
- if (error < 0 && !writes)
- error = git_odb__error_unsupported_in_backend("write pack");
-
- return error;
-}
-
-void *git_odb_backend_malloc(git_odb_backend *backend, size_t len)
-{
- GIT_UNUSED(backend);
- return git__malloc(len);
-}
-
-int git_odb_refresh(struct git_odb *db)
-{
- size_t i;
- assert(db);
-
- for (i = 0; i < db->backends.length; ++i) {
- backend_internal *internal = git_vector_get(&db->backends, i);
- git_odb_backend *b = internal->backend;
-
- if (b->refresh != NULL) {
- int error = b->refresh(b);
- if (error < 0)
- return error;
- }
- }
-
- return 0;
-}
-
-int git_odb__error_notfound(
- const char *message, const git_oid *oid, size_t oid_len)
-{
- if (oid != NULL) {
- char oid_str[GIT_OID_HEXSZ + 1];
- git_oid_tostr(oid_str, oid_len+1, oid);
- giterr_set(GITERR_ODB, "Object not found - %s (%.*s)",
- message, (int) oid_len, oid_str);
- } else
- giterr_set(GITERR_ODB, "Object not found - %s", message);
-
- return GIT_ENOTFOUND;
-}
-
-int git_odb__error_ambiguous(const char *message)
-{
- giterr_set(GITERR_ODB, "Ambiguous SHA1 prefix - %s", message);
- return GIT_EAMBIGUOUS;
-}
-
-int git_odb_init_backend(git_odb_backend *backend, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- backend, version, git_odb_backend, GIT_ODB_BACKEND_INIT);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_odb_h__
-#define INCLUDE_odb_h__
-
-#include "git2/odb.h"
-#include "git2/oid.h"
-#include "git2/types.h"
-
-#include "vector.h"
-#include "cache.h"
-#include "posix.h"
-#include "filter.h"
-
-#define GIT_OBJECTS_DIR "objects/"
-#define GIT_OBJECT_DIR_MODE 0777
-#define GIT_OBJECT_FILE_MODE 0444
-
-/* DO NOT EXPORT */
-typedef struct {
- void *data; /**< Raw, decompressed object data. */
- size_t len; /**< Total number of bytes in data. */
- git_otype type; /**< Type of this object. */
-} git_rawobj;
-
-/* EXPORT */
-struct git_odb_object {
- git_cached_obj cached;
- void *buffer;
-};
-
-/* EXPORT */
-struct git_odb {
- git_refcount rc;
- git_vector backends;
- git_cache own_cache;
-};
-
-/*
- * Hash a git_rawobj internally.
- * The `git_rawobj` is supposed to be previously initialized
- */
-int git_odb__hashobj(git_oid *id, git_rawobj *obj);
-
-/*
- * Format the object header such as it would appear in the on-disk object
- */
-int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type);
-/*
- * Hash an open file descriptor.
- * This is a performance call when the contents of a fd need to be hashed,
- * but the fd is already open and we have the size of the contents.
- *
- * Saves us some `stat` calls.
- *
- * The fd is never closed, not even on error. It must be opened and closed
- * by the caller
- */
-int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type);
-
-/*
- * Hash an open file descriptor applying an array of filters
- * Acts just like git_odb__hashfd with the addition of filters...
- */
-int git_odb__hashfd_filtered(
- git_oid *out, git_file fd, size_t len, git_otype type, git_filter_list *fl);
-
-/*
- * Hash a `path`, assuming it could be a POSIX symlink: if the path is a
- * symlink, then the raw contents of the symlink will be hashed. Otherwise,
- * this will fallback to `git_odb__hashfd`.
- *
- * The hash type for this call is always `GIT_OBJ_BLOB` because symlinks may
- * only point to blobs.
- */
-int git_odb__hashlink(git_oid *out, const char *path);
-
-/*
- * Generate a GIT_ENOTFOUND error for the ODB.
- */
-int git_odb__error_notfound(
- const char *message, const git_oid *oid, size_t oid_len);
-
-/*
- * Generate a GIT_EAMBIGUOUS error for the ODB.
- */
-int git_odb__error_ambiguous(const char *message);
-
-/*
- * Attempt to read object header or just return whole object if it could
- * not be read.
- */
-int git_odb__read_header_or_object(
- git_odb_object **out, size_t *len_p, git_otype *type_p,
- git_odb *db, const git_oid *id);
-
-/* fully free the object; internal method, DO NOT EXPORT */
-void git_odb_object__free(void *object);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include <zlib.h>
-#include "git2/object.h"
-#include "git2/sys/odb_backend.h"
-#include "fileops.h"
-#include "hash.h"
-#include "odb.h"
-#include "delta.h"
-#include "filebuf.h"
-
-#include "git2/odb_backend.h"
-#include "git2/types.h"
-
-typedef struct { /* object header data */
- git_otype type; /* object type */
- size_t size; /* object size */
-} obj_hdr;
-
-typedef struct {
- git_odb_stream stream;
- git_filebuf fbuf;
-} loose_writestream;
-
-typedef struct loose_backend {
- git_odb_backend parent;
-
- int object_zlib_level; /** loose object zlib compression level. */
- int fsync_object_files; /** loose object file fsync flag. */
- mode_t object_file_mode;
- mode_t object_dir_mode;
-
- size_t objects_dirlen;
- char objects_dir[GIT_FLEX_ARRAY];
-} loose_backend;
-
-/* State structure for exploring directories,
- * in order to locate objects matching a short oid.
- */
-typedef struct {
- size_t dir_len;
- unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */
- size_t short_oid_len;
- int found; /* number of matching
- * objects already found */
- unsigned char res_oid[GIT_OID_HEXSZ]; /* hex formatted oid of
- * the object found */
-} loose_locate_object_state;
-
-
-/***********************************************************
- *
- * MISCELLANEOUS HELPER FUNCTIONS
- *
- ***********************************************************/
-
-static int object_file_name(
- git_buf *name, const loose_backend *be, const git_oid *id)
-{
- size_t alloclen;
-
- /* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */
- GITERR_CHECK_ALLOC_ADD(&alloclen, be->objects_dirlen, GIT_OID_HEXSZ);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 3);
- if (git_buf_grow(name, alloclen) < 0)
- return -1;
-
- git_buf_set(name, be->objects_dir, be->objects_dirlen);
- git_path_to_dir(name);
-
- /* loose object filename: aa/aaa... (41 bytes) */
- git_oid_pathfmt(name->ptr + name->size, id);
- name->size += GIT_OID_HEXSZ + 1;
- name->ptr[name->size] = '\0';
-
- return 0;
-}
-
-static int object_mkdir(const git_buf *name, const loose_backend *be)
-{
- return git_futils_mkdir_relative(
- name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode,
- GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL);
-}
-
-static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
-{
- unsigned long c;
- unsigned char *data = (unsigned char *)obj->ptr;
- size_t shift, size, used = 0;
-
- if (git_buf_len(obj) == 0)
- return 0;
-
- c = data[used++];
- hdr->type = (c >> 4) & 7;
-
- size = c & 15;
- shift = 4;
- while (c & 0x80) {
- if (git_buf_len(obj) <= used)
- return 0;
- if (sizeof(size_t) * 8 <= shift)
- return 0;
- c = data[used++];
- size += (c & 0x7f) << shift;
- shift += 7;
- }
- hdr->size = size;
-
- return used;
-}
-
-static size_t get_object_header(obj_hdr *hdr, unsigned char *data)
-{
- char c, typename[10];
- size_t size, used = 0;
-
- /*
- * type name string followed by space.
- */
- while ((c = data[used]) != ' ') {
- typename[used++] = c;
- if (used >= sizeof(typename))
- return 0;
- }
- typename[used] = 0;
- if (used == 0)
- return 0;
- hdr->type = git_object_string2type(typename);
- used++; /* consume the space */
-
- /*
- * length follows immediately in decimal (without
- * leading zeros).
- */
- size = data[used++] - '0';
- if (size > 9)
- return 0;
- if (size) {
- while ((c = data[used]) != '\0') {
- size_t d = c - '0';
- if (d > 9)
- break;
- used++;
- size = size * 10 + d;
- }
- }
- hdr->size = size;
-
- /*
- * the length must be followed by a zero byte
- */
- if (data[used++] != '\0')
- return 0;
-
- return used;
-}
-
-
-
-/***********************************************************
- *
- * ZLIB RELATED FUNCTIONS
- *
- ***********************************************************/
-
-static void init_stream(z_stream *s, void *out, size_t len)
-{
- memset(s, 0, sizeof(*s));
- s->next_out = out;
- s->avail_out = (uInt)len;
-}
-
-static void set_stream_input(z_stream *s, void *in, size_t len)
-{
- s->next_in = in;
- s->avail_in = (uInt)len;
-}
-
-static void set_stream_output(z_stream *s, void *out, size_t len)
-{
- s->next_out = out;
- s->avail_out = (uInt)len;
-}
-
-
-static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
-{
- int status;
-
- init_stream(s, out, len);
- set_stream_input(s, obj->ptr, git_buf_len(obj));
-
- if ((status = inflateInit(s)) < Z_OK)
- return status;
-
- return inflate(s, 0);
-}
-
-static int finish_inflate(z_stream *s)
-{
- int status = Z_OK;
-
- while (status == Z_OK)
- status = inflate(s, Z_FINISH);
-
- inflateEnd(s);
-
- if ((status != Z_STREAM_END) || (s->avail_in != 0)) {
- giterr_set(GITERR_ZLIB, "Failed to finish ZLib inflation. Stream aborted prematurely");
- return -1;
- }
-
- return 0;
-}
-
-static int is_zlib_compressed_data(unsigned char *data)
-{
- unsigned int w;
-
- w = ((unsigned int)(data[0]) << 8) + data[1];
- return (data[0] & 0x8F) == 0x08 && !(w % 31);
-}
-
-static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
-{
- z_stream zs;
- int status = Z_OK;
-
- memset(&zs, 0x0, sizeof(zs));
-
- zs.next_out = out;
- zs.avail_out = (uInt)outlen;
-
- zs.next_in = in;
- zs.avail_in = (uInt)inlen;
-
- if (inflateInit(&zs) < Z_OK) {
- giterr_set(GITERR_ZLIB, "Failed to inflate buffer");
- return -1;
- }
-
- while (status == Z_OK)
- status = inflate(&zs, Z_FINISH);
-
- inflateEnd(&zs);
-
- if (status != Z_STREAM_END /* || zs.avail_in != 0 */ ||
- zs.total_out != outlen)
- {
- giterr_set(GITERR_ZLIB, "Failed to inflate buffer. Stream aborted prematurely");
- return -1;
- }
-
- return 0;
-}
-
-static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
-{
- unsigned char *buf, *head = hb;
- size_t tail, alloc_size;
-
- /*
- * allocate a buffer to hold the inflated data and copy the
- * initial sequence of inflated data from the tail of the
- * head buffer, if any.
- */
- if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr->size, 1) ||
- (buf = git__malloc(alloc_size)) == NULL) {
- inflateEnd(s);
- return NULL;
- }
- tail = s->total_out - used;
- if (used > 0 && tail > 0) {
- if (tail > hdr->size)
- tail = hdr->size;
- memcpy(buf, head + used, tail);
- }
- used = tail;
-
- /*
- * inflate the remainder of the object data, if any
- */
- if (hdr->size < used)
- inflateEnd(s);
- else {
- set_stream_output(s, buf + used, hdr->size - used);
- if (finish_inflate(s)) {
- git__free(buf);
- return NULL;
- }
- }
-
- return buf;
-}
-
-/*
- * At one point, there was a loose object format that was intended to
- * mimic the format used in pack-files. This was to allow easy copying
- * of loose object data into packs. This format is no longer used, but
- * we must still read it.
- */
-static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
-{
- unsigned char *in, *buf;
- obj_hdr hdr;
- size_t len, used, alloclen;
-
- /*
- * read the object header, which is an (uncompressed)
- * binary encoding of the object type and size.
- */
- if ((used = get_binary_object_header(&hdr, obj)) == 0 ||
- !git_object_typeisloose(hdr.type)) {
- giterr_set(GITERR_ODB, "Failed to inflate loose object.");
- return -1;
- }
-
- /*
- * allocate a buffer and inflate the data into it
- */
- GITERR_CHECK_ALLOC_ADD(&alloclen, hdr.size, 1);
- buf = git__malloc(alloclen);
- GITERR_CHECK_ALLOC(buf);
-
- in = ((unsigned char *)obj->ptr) + used;
- len = obj->size - used;
- if (inflate_buffer(in, len, buf, hdr.size) < 0) {
- git__free(buf);
- return -1;
- }
- buf[hdr.size] = '\0';
-
- out->data = buf;
- out->len = hdr.size;
- out->type = hdr.type;
-
- return 0;
-}
-
-static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
-{
- unsigned char head[64], *buf;
- z_stream zs;
- obj_hdr hdr;
- size_t used;
-
- /*
- * check for a pack-like loose object
- */
- if (!is_zlib_compressed_data((unsigned char *)obj->ptr))
- return inflate_packlike_loose_disk_obj(out, obj);
-
- /*
- * inflate the initial part of the io buffer in order
- * to parse the object header (type and size).
- */
- if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK ||
- (used = get_object_header(&hdr, head)) == 0 ||
- !git_object_typeisloose(hdr.type))
- {
- giterr_set(GITERR_ODB, "Failed to inflate disk object.");
- return -1;
- }
-
- /*
- * allocate a buffer and inflate the object data into it
- * (including the initial sequence in the head buffer).
- */
- if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
- return -1;
- buf[hdr.size] = '\0';
-
- out->data = buf;
- out->len = hdr.size;
- out->type = hdr.type;
-
- return 0;
-}
-
-
-
-
-
-
-/***********************************************************
- *
- * ODB OBJECT READING & WRITING
- *
- * Backend for the public API; read headers and full objects
- * from the ODB. Write raw data to the ODB.
- *
- ***********************************************************/
-
-static int read_loose(git_rawobj *out, git_buf *loc)
-{
- int error;
- git_buf obj = GIT_BUF_INIT;
-
- assert(out && loc);
-
- if (git_buf_oom(loc))
- return -1;
-
- out->data = NULL;
- out->len = 0;
- out->type = GIT_OBJ_BAD;
-
- if (!(error = git_futils_readbuffer(&obj, loc->ptr)))
- error = inflate_disk_obj(out, &obj);
-
- git_buf_free(&obj);
-
- return error;
-}
-
-static int read_header_loose(git_rawobj *out, git_buf *loc)
-{
- int error = 0, z_return = Z_ERRNO, read_bytes;
- git_file fd;
- z_stream zs;
- obj_hdr header_obj;
- unsigned char raw_buffer[16], inflated_buffer[64];
-
- assert(out && loc);
-
- if (git_buf_oom(loc))
- return -1;
-
- out->data = NULL;
-
- if ((fd = git_futils_open_ro(loc->ptr)) < 0)
- return fd;
-
- init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
-
- z_return = inflateInit(&zs);
-
- while (z_return == Z_OK) {
- if ((read_bytes = p_read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
- set_stream_input(&zs, raw_buffer, read_bytes);
- z_return = inflate(&zs, 0);
- } else
- z_return = Z_STREAM_END;
- }
-
- if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
- || get_object_header(&header_obj, inflated_buffer) == 0
- || git_object_typeisloose(header_obj.type) == 0)
- {
- giterr_set(GITERR_ZLIB, "Failed to read loose object header");
- error = -1;
- } else {
- out->len = header_obj.size;
- out->type = header_obj.type;
- }
-
- finish_inflate(&zs);
- p_close(fd);
-
- return error;
-}
-
-static int locate_object(
- git_buf *object_location,
- loose_backend *backend,
- const git_oid *oid)
-{
- int error = object_file_name(object_location, backend, oid);
-
- if (!error && !git_path_exists(object_location->ptr))
- return GIT_ENOTFOUND;
-
- return error;
-}
-
-/* Explore an entry of a directory and see if it matches a short oid */
-static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
- loose_locate_object_state *sstate = (loose_locate_object_state *)state;
-
- if (git_buf_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) {
- /* Entry cannot be an object. Continue to next entry */
- return 0;
- }
-
- if (git_path_isdir(pathbuf->ptr) == false) {
- /* We are already in the directory matching the 2 first hex characters,
- * compare the first ncmp characters of the oids */
- if (!memcmp(sstate->short_oid + 2,
- (unsigned char *)pathbuf->ptr + sstate->dir_len,
- sstate->short_oid_len - 2)) {
-
- if (!sstate->found) {
- sstate->res_oid[0] = sstate->short_oid[0];
- sstate->res_oid[1] = sstate->short_oid[1];
- memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2);
- }
- sstate->found++;
- }
- }
-
- if (sstate->found > 1)
- return GIT_EAMBIGUOUS;
-
- return 0;
-}
-
-/* Locate an object matching a given short oid */
-static int locate_object_short_oid(
- git_buf *object_location,
- git_oid *res_oid,
- loose_backend *backend,
- const git_oid *short_oid,
- size_t len)
-{
- char *objects_dir = backend->objects_dir;
- size_t dir_len = strlen(objects_dir), alloc_len;
- loose_locate_object_state state;
- int error;
-
- /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */
- GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3);
- if (git_buf_grow(object_location, alloc_len) < 0)
- return -1;
-
- git_buf_set(object_location, objects_dir, dir_len);
- git_path_to_dir(object_location);
-
- /* save adjusted position at end of dir so it can be restored later */
- dir_len = git_buf_len(object_location);
-
- /* Convert raw oid to hex formatted oid */
- git_oid_fmt((char *)state.short_oid, short_oid);
-
- /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
- if (git_buf_put(object_location, (char *)state.short_oid, 3) < 0)
- return -1;
- object_location->ptr[object_location->size - 1] = '/';
-
- /* Check that directory exists */
- if (git_path_isdir(object_location->ptr) == false)
- return git_odb__error_notfound("no matching loose object for prefix",
- short_oid, len);
-
- state.dir_len = git_buf_len(object_location);
- state.short_oid_len = len;
- state.found = 0;
-
- /* Explore directory to find a unique object matching short_oid */
- error = git_path_direach(
- object_location, 0, fn_locate_object_short_oid, &state);
- if (error < 0 && error != GIT_EAMBIGUOUS)
- return error;
-
- if (!state.found)
- return git_odb__error_notfound("no matching loose object for prefix",
- short_oid, len);
-
- if (state.found > 1)
- return git_odb__error_ambiguous("multiple matches in loose objects");
-
- /* Convert obtained hex formatted oid to raw */
- error = git_oid_fromstr(res_oid, (char *)state.res_oid);
- if (error)
- return error;
-
- /* Update the location according to the oid obtained */
- GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
-
- git_buf_truncate(object_location, dir_len);
- if (git_buf_grow(object_location, alloc_len) < 0)
- return -1;
-
- git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
-
- object_location->size += GIT_OID_HEXSZ + 1;
- object_location->ptr[object_location->size] = '\0';
-
- return 0;
-}
-
-
-
-
-
-
-
-
-
-/***********************************************************
- *
- * LOOSE BACKEND PUBLIC API
- *
- * Implement the git_odb_backend API calls
- *
- ***********************************************************/
-
-static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
-{
- git_buf object_path = GIT_BUF_INIT;
- git_rawobj raw;
- int error;
-
- assert(backend && oid);
-
- raw.len = 0;
- raw.type = GIT_OBJ_BAD;
-
- if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
- error = git_odb__error_notfound("no matching loose object",
- oid, GIT_OID_HEXSZ);
- } else if ((error = read_header_loose(&raw, &object_path)) == 0) {
- *len_p = raw.len;
- *type_p = raw.type;
- }
-
- git_buf_free(&object_path);
-
- return error;
-}
-
-static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
-{
- git_buf object_path = GIT_BUF_INIT;
- git_rawobj raw;
- int error = 0;
-
- assert(backend && oid);
-
- if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
- error = git_odb__error_notfound("no matching loose object",
- oid, GIT_OID_HEXSZ);
- } else if ((error = read_loose(&raw, &object_path)) == 0) {
- *buffer_p = raw.data;
- *len_p = raw.len;
- *type_p = raw.type;
- }
-
- git_buf_free(&object_path);
-
- return error;
-}
-
-static int loose_backend__read_prefix(
- git_oid *out_oid,
- void **buffer_p,
- size_t *len_p,
- git_otype *type_p,
- git_odb_backend *backend,
- const git_oid *short_oid,
- size_t len)
-{
- int error = 0;
-
- assert(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ);
-
- if (len == GIT_OID_HEXSZ) {
- /* We can fall back to regular read method */
- error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
- if (!error)
- git_oid_cpy(out_oid, short_oid);
- } else {
- git_buf object_path = GIT_BUF_INIT;
- git_rawobj raw;
-
- assert(backend && short_oid);
-
- if ((error = locate_object_short_oid(&object_path, out_oid,
- (loose_backend *)backend, short_oid, len)) == 0 &&
- (error = read_loose(&raw, &object_path)) == 0)
- {
- *buffer_p = raw.data;
- *len_p = raw.len;
- *type_p = raw.type;
- }
-
- git_buf_free(&object_path);
- }
-
- return error;
-}
-
-static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
-{
- git_buf object_path = GIT_BUF_INIT;
- int error;
-
- assert(backend && oid);
-
- error = locate_object(&object_path, (loose_backend *)backend, oid);
-
- git_buf_free(&object_path);
-
- return !error;
-}
-
-static int loose_backend__exists_prefix(
- git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
-{
- git_buf object_path = GIT_BUF_INIT;
- int error;
-
- assert(backend && out && short_id && len >= GIT_OID_MINPREFIXLEN);
-
- error = locate_object_short_oid(
- &object_path, out, (loose_backend *)backend, short_id, len);
-
- git_buf_free(&object_path);
-
- return error;
-}
-
-struct foreach_state {
- size_t dir_len;
- git_odb_foreach_cb cb;
- void *data;
-};
-
-GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr)
-{
- int v, i = 0;
- if (strlen(ptr) != GIT_OID_HEXSZ+1)
- return -1;
-
- if (ptr[2] != '/') {
- return -1;
- }
-
- v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]);
- if (v < 0)
- return -1;
-
- oid->id[0] = (unsigned char) v;
-
- ptr += 3;
- for (i = 0; i < 38; i += 2) {
- v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]);
- if (v < 0)
- return -1;
-
- oid->id[1 + i/2] = (unsigned char) v;
- }
-
- return 0;
-}
-
-static int foreach_object_dir_cb(void *_state, git_buf *path)
-{
- git_oid oid;
- struct foreach_state *state = (struct foreach_state *) _state;
-
- if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0)
- return 0;
-
- return giterr_set_after_callback_function(
- state->cb(&oid, state->data), "git_odb_foreach");
-}
-
-static int foreach_cb(void *_state, git_buf *path)
-{
- struct foreach_state *state = (struct foreach_state *) _state;
-
- /* non-dir is some stray file, ignore it */
- if (!git_path_isdir(git_buf_cstr(path)))
- return 0;
-
- return git_path_direach(path, 0, foreach_object_dir_cb, state);
-}
-
-static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
-{
- char *objects_dir;
- int error;
- git_buf buf = GIT_BUF_INIT;
- struct foreach_state state;
- loose_backend *backend = (loose_backend *) _backend;
-
- assert(backend && cb);
-
- objects_dir = backend->objects_dir;
-
- git_buf_sets(&buf, objects_dir);
- git_path_to_dir(&buf);
- if (git_buf_oom(&buf))
- return -1;
-
- memset(&state, 0, sizeof(state));
- state.cb = cb;
- state.data = data;
- state.dir_len = git_buf_len(&buf);
-
- error = git_path_direach(&buf, 0, foreach_cb, &state);
-
- git_buf_free(&buf);
-
- return error;
-}
-
-static int loose_backend__stream_fwrite(git_odb_stream *_stream, const git_oid *oid)
-{
- loose_writestream *stream = (loose_writestream *)_stream;
- loose_backend *backend = (loose_backend *)_stream->backend;
- git_buf final_path = GIT_BUF_INIT;
- int error = 0;
-
- if (object_file_name(&final_path, backend, oid) < 0 ||
- object_mkdir(&final_path, backend) < 0)
- error = -1;
- else
- error = git_filebuf_commit_at(
- &stream->fbuf, final_path.ptr);
-
- git_buf_free(&final_path);
-
- return error;
-}
-
-static int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len)
-{
- loose_writestream *stream = (loose_writestream *)_stream;
- return git_filebuf_write(&stream->fbuf, data, len);
-}
-
-static void loose_backend__stream_free(git_odb_stream *_stream)
-{
- loose_writestream *stream = (loose_writestream *)_stream;
-
- git_filebuf_cleanup(&stream->fbuf);
- git__free(stream);
-}
-
-static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, git_off_t length, git_otype type)
-{
- loose_backend *backend;
- loose_writestream *stream = NULL;
- char hdr[64];
- git_buf tmp_path = GIT_BUF_INIT;
- int hdrlen;
-
- assert(_backend && length >= 0);
-
- backend = (loose_backend *)_backend;
- *stream_out = NULL;
-
- hdrlen = git_odb__format_object_header(hdr, sizeof(hdr), length, type);
-
- stream = git__calloc(1, sizeof(loose_writestream));
- GITERR_CHECK_ALLOC(stream);
-
- stream->stream.backend = _backend;
- stream->stream.read = NULL; /* read only */
- stream->stream.write = &loose_backend__stream_write;
- stream->stream.finalize_write = &loose_backend__stream_fwrite;
- stream->stream.free = &loose_backend__stream_free;
- stream->stream.mode = GIT_STREAM_WRONLY;
-
- if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
- git_filebuf_open(&stream->fbuf, tmp_path.ptr,
- GIT_FILEBUF_TEMPORARY |
- (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT),
- backend->object_file_mode) < 0 ||
- stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
- {
- git_filebuf_cleanup(&stream->fbuf);
- git__free(stream);
- stream = NULL;
- }
- git_buf_free(&tmp_path);
- *stream_out = (git_odb_stream *)stream;
-
- return !stream ? -1 : 0;
-}
-
-static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_otype type)
-{
- int error = 0, header_len;
- git_buf final_path = GIT_BUF_INIT;
- char header[64];
- git_filebuf fbuf = GIT_FILEBUF_INIT;
- loose_backend *backend;
-
- backend = (loose_backend *)_backend;
-
- /* prepare the header for the file */
- header_len = git_odb__format_object_header(header, sizeof(header), len, type);
-
- if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
- git_filebuf_open(&fbuf, final_path.ptr,
- GIT_FILEBUF_TEMPORARY |
- (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT),
- backend->object_file_mode) < 0)
- {
- error = -1;
- goto cleanup;
- }
-
- git_filebuf_write(&fbuf, header, header_len);
- git_filebuf_write(&fbuf, data, len);
-
- if (object_file_name(&final_path, backend, oid) < 0 ||
- object_mkdir(&final_path, backend) < 0 ||
- git_filebuf_commit_at(&fbuf, final_path.ptr) < 0)
- error = -1;
-
-cleanup:
- if (error < 0)
- git_filebuf_cleanup(&fbuf);
- git_buf_free(&final_path);
- return error;
-}
-
-static int loose_backend__freshen(
- git_odb_backend *_backend,
- const git_oid *oid)
-{
- loose_backend *backend = (loose_backend *)_backend;
- git_buf path = GIT_BUF_INIT;
- int error;
-
- if (object_file_name(&path, backend, oid) < 0)
- return -1;
-
- error = git_futils_touch(path.ptr, NULL);
- git_buf_free(&path);
-
- return error;
-}
-
-static void loose_backend__free(git_odb_backend *_backend)
-{
- loose_backend *backend;
- assert(_backend);
- backend = (loose_backend *)_backend;
-
- git__free(backend);
-}
-
-int git_odb_backend_loose(
- git_odb_backend **backend_out,
- const char *objects_dir,
- int compression_level,
- int do_fsync,
- unsigned int dir_mode,
- unsigned int file_mode)
-{
- loose_backend *backend;
- size_t objects_dirlen, alloclen;
-
- assert(backend_out && objects_dir);
-
- objects_dirlen = strlen(objects_dir);
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(loose_backend), objects_dirlen);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 2);
- backend = git__calloc(1, alloclen);
- GITERR_CHECK_ALLOC(backend);
-
- backend->parent.version = GIT_ODB_BACKEND_VERSION;
- backend->objects_dirlen = objects_dirlen;
- memcpy(backend->objects_dir, objects_dir, objects_dirlen);
- if (backend->objects_dir[backend->objects_dirlen - 1] != '/')
- backend->objects_dir[backend->objects_dirlen++] = '/';
-
- if (compression_level < 0)
- compression_level = Z_BEST_SPEED;
-
- if (dir_mode == 0)
- dir_mode = GIT_OBJECT_DIR_MODE;
-
- if (file_mode == 0)
- file_mode = GIT_OBJECT_FILE_MODE;
-
- backend->object_zlib_level = compression_level;
- backend->fsync_object_files = do_fsync;
- backend->object_dir_mode = dir_mode;
- backend->object_file_mode = file_mode;
-
- backend->parent.read = &loose_backend__read;
- backend->parent.write = &loose_backend__write;
- backend->parent.read_prefix = &loose_backend__read_prefix;
- backend->parent.read_header = &loose_backend__read_header;
- backend->parent.writestream = &loose_backend__stream;
- backend->parent.exists = &loose_backend__exists;
- backend->parent.exists_prefix = &loose_backend__exists_prefix;
- backend->parent.foreach = &loose_backend__foreach;
- backend->parent.freshen = &loose_backend__freshen;
- backend->parent.free = &loose_backend__free;
-
- *backend_out = (git_odb_backend *)backend;
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "git2/object.h"
-#include "git2/sys/odb_backend.h"
-#include "fileops.h"
-#include "hash.h"
-#include "odb.h"
-#include "array.h"
-#include "oidmap.h"
-
-#include "git2/odb_backend.h"
-#include "git2/types.h"
-#include "git2/pack.h"
-
-GIT__USE_OIDMAP
-
-struct memobject {
- git_oid oid;
- size_t len;
- git_otype type;
- char data[GIT_FLEX_ARRAY];
-};
-
-struct memory_packer_db {
- git_odb_backend parent;
- git_oidmap *objects;
- git_array_t(struct memobject *) commits;
-};
-
-static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_otype type)
-{
- struct memory_packer_db *db = (struct memory_packer_db *)_backend;
- struct memobject *obj = NULL;
- khiter_t pos;
- size_t alloc_len;
- int rval;
-
- pos = kh_put(oid, db->objects, oid, &rval);
- if (rval < 0)
- return -1;
-
- if (rval == 0)
- return 0;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(struct memobject), len);
- obj = git__malloc(alloc_len);
- GITERR_CHECK_ALLOC(obj);
-
- memcpy(obj->data, data, len);
- git_oid_cpy(&obj->oid, oid);
- obj->len = len;
- obj->type = type;
-
- kh_key(db->objects, pos) = &obj->oid;
- kh_val(db->objects, pos) = obj;
-
- if (type == GIT_OBJ_COMMIT) {
- struct memobject **store = git_array_alloc(db->commits);
- GITERR_CHECK_ALLOC(store);
- *store = obj;
- }
-
- return 0;
-}
-
-static int impl__exists(git_odb_backend *backend, const git_oid *oid)
-{
- struct memory_packer_db *db = (struct memory_packer_db *)backend;
- khiter_t pos;
-
- pos = kh_get(oid, db->objects, oid);
- if (pos != kh_end(db->objects))
- return 1;
-
- return 0;
-}
-
-static int impl__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
-{
- struct memory_packer_db *db = (struct memory_packer_db *)backend;
- struct memobject *obj = NULL;
- khiter_t pos;
-
- pos = kh_get(oid, db->objects, oid);
- if (pos == kh_end(db->objects))
- return GIT_ENOTFOUND;
-
- obj = kh_val(db->objects, pos);
-
- *len_p = obj->len;
- *type_p = obj->type;
- *buffer_p = git__malloc(obj->len);
- GITERR_CHECK_ALLOC(*buffer_p);
-
- memcpy(*buffer_p, obj->data, obj->len);
- return 0;
-}
-
-static int impl__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
-{
- struct memory_packer_db *db = (struct memory_packer_db *)backend;
- struct memobject *obj = NULL;
- khiter_t pos;
-
- pos = kh_get(oid, db->objects, oid);
- if (pos == kh_end(db->objects))
- return GIT_ENOTFOUND;
-
- obj = kh_val(db->objects, pos);
-
- *len_p = obj->len;
- *type_p = obj->type;
- return 0;
-}
-
-int git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *_backend)
-{
- struct memory_packer_db *db = (struct memory_packer_db *)_backend;
- git_packbuilder *packbuilder;
- uint32_t i;
- int err = -1;
-
- if (git_packbuilder_new(&packbuilder, repo) < 0)
- return -1;
-
- for (i = 0; i < db->commits.size; ++i) {
- struct memobject *commit = db->commits.ptr[i];
-
- err = git_packbuilder_insert_commit(packbuilder, &commit->oid);
- if (err < 0)
- goto cleanup;
- }
-
- err = git_packbuilder_write_buf(pack, packbuilder);
-
-cleanup:
- git_packbuilder_free(packbuilder);
- return err;
-}
-
-void git_mempack_reset(git_odb_backend *_backend)
-{
- struct memory_packer_db *db = (struct memory_packer_db *)_backend;
- struct memobject *object = NULL;
-
- kh_foreach_value(db->objects, object, {
- git__free(object);
- });
-
- git_array_clear(db->commits);
-
- git_oidmap_clear(db->objects);
-}
-
-static void impl__free(git_odb_backend *_backend)
-{
- struct memory_packer_db *db = (struct memory_packer_db *)_backend;
-
- git_oidmap_free(db->objects);
- git__free(db);
-}
-
-int git_mempack_new(git_odb_backend **out)
-{
- struct memory_packer_db *db;
-
- assert(out);
-
- db = git__calloc(1, sizeof(struct memory_packer_db));
- GITERR_CHECK_ALLOC(db);
-
- db->objects = git_oidmap_alloc();
-
- db->parent.version = GIT_ODB_BACKEND_VERSION;
- db->parent.read = &impl__read;
- db->parent.write = &impl__write;
- db->parent.read_header = &impl__read_header;
- db->parent.exists = &impl__exists;
- db->parent.free = &impl__free;
-
- *out = (git_odb_backend *)db;
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include <zlib.h>
-#include "git2/repository.h"
-#include "git2/indexer.h"
-#include "git2/sys/odb_backend.h"
-#include "fileops.h"
-#include "hash.h"
-#include "odb.h"
-#include "delta.h"
-#include "sha1_lookup.h"
-#include "mwindow.h"
-#include "pack.h"
-
-#include "git2/odb_backend.h"
-
-/* re-freshen pack files no more than every 2 seconds */
-#define FRESHEN_FREQUENCY 2
-
-struct pack_backend {
- git_odb_backend parent;
- git_vector packs;
- struct git_pack_file *last_found;
- char *pack_folder;
-};
-
-struct pack_writepack {
- struct git_odb_writepack parent;
- git_indexer *indexer;
-};
-
-/**
- * The wonderful tale of a Packed Object lookup query
- * ===================================================
- * A riveting and epic story of epicness and ASCII
- * art, presented by yours truly,
- * Sir Vicent of Marti
- *
- *
- * Chapter 1: Once upon a time...
- * Initialization of the Pack Backend
- * --------------------------------------------------
- *
- * # git_odb_backend_pack
- * | Creates the pack backend structure, initializes the
- * | callback pointers to our default read() and exist() methods,
- * | and tries to preload all the known packfiles in the ODB.
- * |
- * |-# packfile_load_all
- * | Tries to find the `pack` folder, if it exists. ODBs without
- * | a pack folder are ignored altogether. If there's a `pack` folder
- * | we run a `dirent` callback through every file in the pack folder
- * | to find our packfiles. The packfiles are then sorted according
- * | to a sorting callback.
- * |
- * |-# packfile_load__cb
- * | | This callback is called from `dirent` with every single file
- * | | inside the pack folder. We find the packs by actually locating
- * | | their index (ends in ".idx"). From that index, we verify that
- * | | the corresponding packfile exists and is valid, and if so, we
- * | | add it to the pack list.
- * | |
- * | |-# packfile_check
- * | Make sure that there's a packfile to back this index, and store
- * | some very basic information regarding the packfile itself,
- * | such as the full path, the size, and the modification time.
- * | We don't actually open the packfile to check for internal consistency.
- * |
- * |-# packfile_sort__cb
- * Sort all the preloaded packs according to some specific criteria:
- * we prioritize the "newer" packs because it's more likely they
- * contain the objects we are looking for, and we prioritize local
- * packs over remote ones.
- *
- *
- *
- * Chapter 2: To be, or not to be...
- * A standard packed `exist` query for an OID
- * --------------------------------------------------
- *
- * # pack_backend__exists
- * | Check if the given SHA1 oid exists in any of the packs
- * | that have been loaded for our ODB.
- * |
- * |-# pack_entry_find
- * | Iterate through all the packs that have been preloaded
- * | (starting by the pack where the latest object was found)
- * | to try to find the OID in one of them.
- * |
- * |-# pack_entry_find1
- * | Check the index of an individual pack to see if the SHA1
- * | OID can be found. If we can find the offset to that SHA1
- * | inside of the index, that means the object is contained
- * | inside of the packfile and we can stop searching.
- * | Before returning, we verify that the packfile behing the
- * | index we are searching still exists on disk.
- * |
- * |-# pack_entry_find_offset
- * | | Mmap the actual index file to disk if it hasn't been opened
- * | | yet, and run a binary search through it to find the OID.
- * | | See <http://book.git-scm.com/7_the_packfile.html> for specifics
- * | | on the Packfile Index format and how do we find entries in it.
- * | |
- * | |-# pack_index_open
- * | | Guess the name of the index based on the full path to the
- * | | packfile, open it and verify its contents. Only if the index
- * | | has not been opened already.
- * | |
- * | |-# pack_index_check
- * | Mmap the index file and do a quick run through the header
- * | to guess the index version (right now we support v1 and v2),
- * | and to verify that the size of the index makes sense.
- * |
- * |-# packfile_open
- * See `packfile_open` in Chapter 3
- *
- *
- *
- * Chapter 3: The neverending story...
- * A standard packed `lookup` query for an OID
- * --------------------------------------------------
- * TODO
- *
- */
-
-
-/***********************************************************
- *
- * FORWARD DECLARATIONS
- *
- ***********************************************************/
-
-static int packfile_sort__cb(const void *a_, const void *b_);
-
-static int packfile_load__cb(void *_data, git_buf *path);
-
-static int pack_entry_find(struct git_pack_entry *e,
- struct pack_backend *backend, const git_oid *oid);
-
-/* Can find the offset of an object given
- * a prefix of an identifier.
- * Sets GIT_EAMBIGUOUS if short oid is ambiguous.
- * This method assumes that len is between
- * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
- */
-static int pack_entry_find_prefix(
- struct git_pack_entry *e,
- struct pack_backend *backend,
- const git_oid *short_oid,
- size_t len);
-
-
-
-/***********************************************************
- *
- * PACK WINDOW MANAGEMENT
- *
- ***********************************************************/
-
-static int packfile_sort__cb(const void *a_, const void *b_)
-{
- const struct git_pack_file *a = a_;
- const struct git_pack_file *b = b_;
- int st;
-
- /*
- * Local packs tend to contain objects specific to our
- * variant of the project than remote ones. In addition,
- * remote ones could be on a network mounted filesystem.
- * Favor local ones for these reasons.
- */
- st = a->pack_local - b->pack_local;
- if (st)
- return -st;
-
- /*
- * Younger packs tend to contain more recent objects,
- * and more recent objects tend to get accessed more
- * often.
- */
- if (a->mtime < b->mtime)
- return 1;
- else if (a->mtime == b->mtime)
- return 0;
-
- return -1;
-}
-
-
-static int packfile_load__cb(void *data, git_buf *path)
-{
- struct pack_backend *backend = data;
- struct git_pack_file *pack;
- const char *path_str = git_buf_cstr(path);
- size_t i, cmp_len = git_buf_len(path);
- int error;
-
- if (cmp_len <= strlen(".idx") || git__suffixcmp(path_str, ".idx") != 0)
- return 0; /* not an index */
-
- cmp_len -= strlen(".idx");
-
- for (i = 0; i < backend->packs.length; ++i) {
- struct git_pack_file *p = git_vector_get(&backend->packs, i);
-
- if (memcmp(p->pack_name, path_str, cmp_len) == 0)
- return 0;
- }
-
- error = git_mwindow_get_pack(&pack, path->ptr);
-
- /* ignore missing .pack file as git does */
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- return 0;
- }
-
- if (!error)
- error = git_vector_insert(&backend->packs, pack);
-
- return error;
-
-}
-
-static int pack_entry_find_inner(
- struct git_pack_entry *e,
- struct pack_backend *backend,
- const git_oid *oid,
- struct git_pack_file *last_found)
-{
- size_t i;
-
- if (last_found &&
- git_pack_entry_find(e, last_found, oid, GIT_OID_HEXSZ) == 0)
- return 0;
-
- for (i = 0; i < backend->packs.length; ++i) {
- struct git_pack_file *p;
-
- p = git_vector_get(&backend->packs, i);
- if (p == last_found)
- continue;
-
- if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) {
- backend->last_found = p;
- return 0;
- }
- }
-
- return -1;
-}
-
-static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
-{
- struct git_pack_file *last_found = backend->last_found;
-
- if (backend->last_found &&
- git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == 0)
- return 0;
-
- if (!pack_entry_find_inner(e, backend, oid, last_found))
- return 0;
-
- return git_odb__error_notfound(
- "failed to find pack entry", oid, GIT_OID_HEXSZ);
-}
-
-static int pack_entry_find_prefix(
- struct git_pack_entry *e,
- struct pack_backend *backend,
- const git_oid *short_oid,
- size_t len)
-{
- int error;
- size_t i;
- git_oid found_full_oid = {{0}};
- bool found = false;
- struct git_pack_file *last_found = backend->last_found;
-
- if (last_found) {
- error = git_pack_entry_find(e, last_found, short_oid, len);
- if (error == GIT_EAMBIGUOUS)
- return error;
- if (!error) {
- git_oid_cpy(&found_full_oid, &e->sha1);
- found = true;
- }
- }
-
- for (i = 0; i < backend->packs.length; ++i) {
- struct git_pack_file *p;
-
- p = git_vector_get(&backend->packs, i);
- if (p == last_found)
- continue;
-
- error = git_pack_entry_find(e, p, short_oid, len);
- if (error == GIT_EAMBIGUOUS)
- return error;
- if (!error) {
- if (found && git_oid_cmp(&e->sha1, &found_full_oid))
- return git_odb__error_ambiguous("found multiple pack entries");
- git_oid_cpy(&found_full_oid, &e->sha1);
- found = true;
- backend->last_found = p;
- }
- }
-
- if (!found)
- return git_odb__error_notfound("no matching pack entry for prefix",
- short_oid, len);
- else
- return 0;
-}
-
-
-/***********************************************************
- *
- * PACKED BACKEND PUBLIC API
- *
- * Implement the git_odb_backend API calls
- *
- ***********************************************************/
-static int pack_backend__refresh(git_odb_backend *backend_)
-{
- int error;
- struct stat st;
- git_buf path = GIT_BUF_INIT;
- struct pack_backend *backend = (struct pack_backend *)backend_;
-
- if (backend->pack_folder == NULL)
- return 0;
-
- if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
- return git_odb__error_notfound("failed to refresh packfiles", NULL, 0);
-
- git_buf_sets(&path, backend->pack_folder);
-
- /* reload all packs */
- error = git_path_direach(&path, 0, packfile_load__cb, backend);
-
- git_buf_free(&path);
- git_vector_sort(&backend->packs);
-
- return error;
-}
-
-static int pack_backend__read_header(
- size_t *len_p, git_otype *type_p,
- struct git_odb_backend *backend, const git_oid *oid)
-{
- struct git_pack_entry e;
- int error;
-
- assert(len_p && type_p && backend && oid);
-
- if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
- return error;
-
- return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
-}
-
-static int pack_backend__freshen(
- git_odb_backend *backend, const git_oid *oid)
-{
- struct git_pack_entry e;
- time_t now;
- int error;
-
- if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
- return error;
-
- now = time(NULL);
-
- if (e.p->last_freshen > now - FRESHEN_FREQUENCY)
- return 0;
-
- if ((error = git_futils_touch(e.p->pack_name, &now)) < 0)
- return error;
-
- e.p->last_freshen = now;
- return 0;
-}
-
-static int pack_backend__read(
- void **buffer_p, size_t *len_p, git_otype *type_p,
- git_odb_backend *backend, const git_oid *oid)
-{
- struct git_pack_entry e;
- git_rawobj raw = {NULL};
- int error;
-
- if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 ||
- (error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0)
- return error;
-
- *buffer_p = raw.data;
- *len_p = raw.len;
- *type_p = raw.type;
-
- return 0;
-}
-
-static int pack_backend__read_prefix(
- git_oid *out_oid,
- void **buffer_p,
- size_t *len_p,
- git_otype *type_p,
- git_odb_backend *backend,
- const git_oid *short_oid,
- size_t len)
-{
- int error = 0;
-
- if (len < GIT_OID_MINPREFIXLEN)
- error = git_odb__error_ambiguous("prefix length too short");
-
- else if (len >= GIT_OID_HEXSZ) {
- /* We can fall back to regular read method */
- error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
- if (!error)
- git_oid_cpy(out_oid, short_oid);
- } else {
- struct git_pack_entry e;
- git_rawobj raw;
-
- if ((error = pack_entry_find_prefix(
- &e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
- (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
- {
- *buffer_p = raw.data;
- *len_p = raw.len;
- *type_p = raw.type;
- git_oid_cpy(out_oid, &e.sha1);
- }
- }
-
- return error;
-}
-
-static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
-{
- struct git_pack_entry e;
- return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
-}
-
-static int pack_backend__exists_prefix(
- git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
-{
- int error;
- struct pack_backend *pb = (struct pack_backend *)backend;
- struct git_pack_entry e = {0};
-
- error = pack_entry_find_prefix(&e, pb, short_id, len);
- git_oid_cpy(out, &e.sha1);
- return error;
-}
-
-static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
-{
- int error;
- struct git_pack_file *p;
- struct pack_backend *backend;
- unsigned int i;
-
- assert(_backend && cb);
- backend = (struct pack_backend *)_backend;
-
- /* Make sure we know about the packfiles */
- if ((error = pack_backend__refresh(_backend)) < 0)
- return error;
-
- git_vector_foreach(&backend->packs, i, p) {
- if ((error = git_pack_foreach_entry(p, cb, data)) < 0)
- return error;
- }
-
- return 0;
-}
-
-static int pack_backend__writepack_append(struct git_odb_writepack *_writepack, const void *data, size_t size, git_transfer_progress *stats)
-{
- struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
-
- assert(writepack);
-
- return git_indexer_append(writepack->indexer, data, size, stats);
-}
-
-static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack, git_transfer_progress *stats)
-{
- struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
-
- assert(writepack);
-
- return git_indexer_commit(writepack->indexer, stats);
-}
-
-static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
-{
- struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
-
- assert(writepack);
-
- git_indexer_free(writepack->indexer);
- git__free(writepack);
-}
-
-static int pack_backend__writepack(struct git_odb_writepack **out,
- git_odb_backend *_backend,
- git_odb *odb,
- git_transfer_progress_cb progress_cb,
- void *progress_payload)
-{
- struct pack_backend *backend;
- struct pack_writepack *writepack;
-
- assert(out && _backend);
-
- *out = NULL;
-
- backend = (struct pack_backend *)_backend;
-
- writepack = git__calloc(1, sizeof(struct pack_writepack));
- GITERR_CHECK_ALLOC(writepack);
-
- if (git_indexer_new(&writepack->indexer,
- backend->pack_folder, 0, odb, progress_cb, progress_payload) < 0) {
- git__free(writepack);
- return -1;
- }
-
- writepack->parent.backend = _backend;
- writepack->parent.append = pack_backend__writepack_append;
- writepack->parent.commit = pack_backend__writepack_commit;
- writepack->parent.free = pack_backend__writepack_free;
-
- *out = (git_odb_writepack *)writepack;
-
- return 0;
-}
-
-static void pack_backend__free(git_odb_backend *_backend)
-{
- struct pack_backend *backend;
- size_t i;
-
- assert(_backend);
-
- backend = (struct pack_backend *)_backend;
-
- for (i = 0; i < backend->packs.length; ++i) {
- struct git_pack_file *p = git_vector_get(&backend->packs, i);
- git_mwindow_put_pack(p);
- }
-
- git_vector_free(&backend->packs);
- git__free(backend->pack_folder);
- git__free(backend);
-}
-
-static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
-{
- struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend));
- GITERR_CHECK_ALLOC(backend);
-
- if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) {
- git__free(backend);
- return -1;
- }
-
- backend->parent.version = GIT_ODB_BACKEND_VERSION;
-
- backend->parent.read = &pack_backend__read;
- backend->parent.read_prefix = &pack_backend__read_prefix;
- backend->parent.read_header = &pack_backend__read_header;
- backend->parent.exists = &pack_backend__exists;
- backend->parent.exists_prefix = &pack_backend__exists_prefix;
- backend->parent.refresh = &pack_backend__refresh;
- backend->parent.foreach = &pack_backend__foreach;
- backend->parent.writepack = &pack_backend__writepack;
- backend->parent.freshen = &pack_backend__freshen;
- backend->parent.free = &pack_backend__free;
-
- *out = backend;
- return 0;
-}
-
-int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
-{
- struct pack_backend *backend = NULL;
- struct git_pack_file *packfile = NULL;
-
- if (pack_backend__alloc(&backend, 1) < 0)
- return -1;
-
- if (git_mwindow_get_pack(&packfile, idx) < 0 ||
- git_vector_insert(&backend->packs, packfile) < 0)
- {
- pack_backend__free((git_odb_backend *)backend);
- return -1;
- }
-
- *backend_out = (git_odb_backend *)backend;
- return 0;
-}
-
-int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
-{
- int error = 0;
- struct pack_backend *backend = NULL;
- git_buf path = GIT_BUF_INIT;
-
- if (pack_backend__alloc(&backend, 8) < 0)
- return -1;
-
- if (!(error = git_buf_joinpath(&path, objects_dir, "pack")) &&
- git_path_isdir(git_buf_cstr(&path)))
- {
- backend->pack_folder = git_buf_detach(&path);
- error = pack_backend__refresh((git_odb_backend *)backend);
- }
-
- if (error < 0) {
- pack_backend__free((git_odb_backend *)backend);
- backend = NULL;
- }
-
- *backend_out = (git_odb_backend *)backend;
-
- git_buf_free(&path);
-
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) 2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_offmap_h__
-#define INCLUDE_offmap_h__
-
-#include "common.h"
-#include "git2/types.h"
-
-#define kmalloc git__malloc
-#define kcalloc git__calloc
-#define krealloc git__realloc
-#define kreallocarray git__reallocarray
-#define kfree git__free
-#include "khash.h"
-
-__KHASH_TYPE(off, git_off_t, void *)
-typedef khash_t(off) git_offmap;
-
-#define GIT__USE_OFFMAP \
- __KHASH_IMPL(off, static kh_inline, git_off_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal)
-
-#define git_offmap_alloc() kh_init(off)
-#define git_offmap_free(h) kh_destroy(off, h), h = NULL
-#define git_offmap_clear(h) kh_clear(off, h)
-
-#define git_offmap_num_entries(h) kh_size(h)
-
-#define git_offmap_lookup_index(h, k) kh_get(off, h, k)
-#define git_offmap_valid_index(h, idx) (idx != kh_end(h))
-
-#define git_offmap_exists(h, k) (kh_get(off, h, k) != kh_end(h))
-
-#define git_offmap_value_at(h, idx) kh_val(h, idx)
-#define git_offmap_set_value_at(h, idx, v) kh_val(h, idx) = v
-#define git_offmap_delete_at(h, idx) kh_del(off, h, idx)
-
-#define git_offmap_insert(h, key, val, rval) do { \
- khiter_t __pos = kh_put(off, h, key, &rval); \
- if (rval >= 0) { \
- if (rval == 0) kh_key(h, __pos) = key; \
- kh_val(h, __pos) = val; \
- } } while (0)
-
-#define git_offmap_insert2(h, key, val, oldv, rval) do { \
- khiter_t __pos = kh_put(off, h, key, &rval); \
- if (rval >= 0) { \
- if (rval == 0) { \
- oldv = kh_val(h, __pos); \
- kh_key(h, __pos) = key; \
- } else { oldv = NULL; } \
- kh_val(h, __pos) = val; \
- } } while (0)
-
-#define git_offmap_delete(h, key) do { \
- khiter_t __pos = git_offmap_lookup_index(h, key); \
- if (git_offmap_valid_index(h, __pos)) \
- git_offmap_delete_at(h, __pos); } while (0)
-
-#define git_offmap_foreach kh_foreach
-#define git_offmap_foreach_value kh_foreach_value
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "git2/oid.h"
-#include "repository.h"
-#include "global.h"
-#include <string.h>
-#include <limits.h>
-
-static char to_hex[] = "0123456789abcdef";
-
-static int oid_error_invalid(const char *msg)
-{
- giterr_set(GITERR_INVALID, "Unable to parse OID - %s", msg);
- return -1;
-}
-
-int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
-{
- size_t p;
- int v;
-
- assert(out && str);
-
- if (!length)
- return oid_error_invalid("too short");
-
- if (length > GIT_OID_HEXSZ)
- return oid_error_invalid("too long");
-
- memset(out->id, 0, GIT_OID_RAWSZ);
-
- for (p = 0; p < length; p++) {
- v = git__fromhex(str[p]);
- if (v < 0)
- return oid_error_invalid("contains invalid characters");
-
- out->id[p / 2] |= (unsigned char)(v << (p % 2 ? 0 : 4));
- }
-
- return 0;
-}
-
-int git_oid_fromstrp(git_oid *out, const char *str)
-{
- return git_oid_fromstrn(out, str, strlen(str));
-}
-
-int git_oid_fromstr(git_oid *out, const char *str)
-{
- return git_oid_fromstrn(out, str, GIT_OID_HEXSZ);
-}
-
-GIT_INLINE(char) *fmt_one(char *str, unsigned int val)
-{
- *str++ = to_hex[val >> 4];
- *str++ = to_hex[val & 0xf];
- return str;
-}
-
-void git_oid_nfmt(char *str, size_t n, const git_oid *oid)
-{
- size_t i, max_i;
-
- if (!oid) {
- memset(str, 0, n);
- return;
- }
- if (n > GIT_OID_HEXSZ) {
- memset(&str[GIT_OID_HEXSZ], 0, n - GIT_OID_HEXSZ);
- n = GIT_OID_HEXSZ;
- }
-
- max_i = n / 2;
-
- for (i = 0; i < max_i; i++)
- str = fmt_one(str, oid->id[i]);
-
- if (n & 1)
- *str++ = to_hex[oid->id[i] >> 4];
-}
-
-void git_oid_fmt(char *str, const git_oid *oid)
-{
- git_oid_nfmt(str, GIT_OID_HEXSZ, oid);
-}
-
-void git_oid_pathfmt(char *str, const git_oid *oid)
-{
- size_t i;
-
- str = fmt_one(str, oid->id[0]);
- *str++ = '/';
- for (i = 1; i < sizeof(oid->id); i++)
- str = fmt_one(str, oid->id[i]);
-}
-
-char *git_oid_tostr_s(const git_oid *oid)
-{
- char *str = GIT_GLOBAL->oid_fmt;
- git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid);
- return str;
-}
-
-char *git_oid_allocfmt(const git_oid *oid)
-{
- char *str = git__malloc(GIT_OID_HEXSZ + 1);
- if (!str)
- return NULL;
- git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid);
- return str;
-}
-
-char *git_oid_tostr(char *out, size_t n, const git_oid *oid)
-{
- if (!out || n == 0)
- return "";
-
- if (n > GIT_OID_HEXSZ + 1)
- n = GIT_OID_HEXSZ + 1;
-
- git_oid_nfmt(out, n - 1, oid); /* allow room for terminating NUL */
- out[n - 1] = '\0';
-
- return out;
-}
-
-int git_oid__parse(
- git_oid *oid, const char **buffer_out,
- const char *buffer_end, const char *header)
-{
- const size_t sha_len = GIT_OID_HEXSZ;
- const size_t header_len = strlen(header);
-
- const char *buffer = *buffer_out;
-
- if (buffer + (header_len + sha_len + 1) > buffer_end)
- return -1;
-
- if (memcmp(buffer, header, header_len) != 0)
- return -1;
-
- if (buffer[header_len + sha_len] != '\n')
- return -1;
-
- if (git_oid_fromstr(oid, buffer + header_len) < 0)
- return -1;
-
- *buffer_out = buffer + (header_len + sha_len + 1);
-
- return 0;
-}
-
-void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid)
-{
- char hex_oid[GIT_OID_HEXSZ];
-
- git_oid_fmt(hex_oid, oid);
- git_buf_puts(buf, header);
- git_buf_put(buf, hex_oid, GIT_OID_HEXSZ);
- git_buf_putc(buf, '\n');
-}
-
-void git_oid_fromraw(git_oid *out, const unsigned char *raw)
-{
- memcpy(out->id, raw, sizeof(out->id));
-}
-
-void git_oid_cpy(git_oid *out, const git_oid *src)
-{
- memcpy(out->id, src->id, sizeof(out->id));
-}
-
-int git_oid_cmp(const git_oid *a, const git_oid *b)
-{
- return git_oid__cmp(a, b);
-}
-
-int git_oid_equal(const git_oid *a, const git_oid *b)
-{
- return (git_oid__cmp(a, b) == 0);
-}
-
-int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len)
-{
- const unsigned char *a = oid_a->id;
- const unsigned char *b = oid_b->id;
-
- if (len > GIT_OID_HEXSZ)
- len = GIT_OID_HEXSZ;
-
- while (len > 1) {
- if (*a != *b)
- return 1;
- a++;
- b++;
- len -= 2;
- };
-
- if (len)
- if ((*a ^ *b) & 0xf0)
- return 1;
-
- return 0;
-}
-
-int git_oid_strcmp(const git_oid *oid_a, const char *str)
-{
- const unsigned char *a;
- unsigned char strval;
- int hexval;
-
- for (a = oid_a->id; *str && (a - oid_a->id) < GIT_OID_RAWSZ; ++a) {
- if ((hexval = git__fromhex(*str++)) < 0)
- return -1;
- strval = (unsigned char)(hexval << 4);
- if (*str) {
- if ((hexval = git__fromhex(*str++)) < 0)
- return -1;
- strval |= hexval;
- }
- if (*a != strval)
- return (*a - strval);
- }
-
- return 0;
-}
-
-int git_oid_streq(const git_oid *oid_a, const char *str)
-{
- return git_oid_strcmp(oid_a, str) == 0 ? 0 : -1;
-}
-
-int git_oid_iszero(const git_oid *oid_a)
-{
- const unsigned char *a = oid_a->id;
- unsigned int i;
- for (i = 0; i < GIT_OID_RAWSZ; ++i, ++a)
- if (*a != 0)
- return 0;
- return 1;
-}
-
-typedef short node_index;
-
-typedef union {
- const char *tail;
- node_index children[16];
-} trie_node;
-
-struct git_oid_shorten {
- trie_node *nodes;
- size_t node_count, size;
- int min_length, full;
-};
-
-static int resize_trie(git_oid_shorten *self, size_t new_size)
-{
- self->nodes = git__reallocarray(self->nodes, new_size, sizeof(trie_node));
- GITERR_CHECK_ALLOC(self->nodes);
-
- if (new_size > self->size) {
- memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(trie_node));
- }
-
- self->size = new_size;
- return 0;
-}
-
-static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, const char *oid)
-{
- trie_node *node, *leaf;
- node_index idx_leaf;
-
- if (os->node_count >= os->size) {
- if (resize_trie(os, os->size * 2) < 0)
- return NULL;
- }
-
- idx_leaf = (node_index)os->node_count++;
-
- if (os->node_count == SHRT_MAX) {
- os->full = 1;
- return NULL;
- }
-
- node = &os->nodes[idx];
- node->children[push_at] = -idx_leaf;
-
- leaf = &os->nodes[idx_leaf];
- leaf->tail = oid;
-
- return node;
-}
-
-git_oid_shorten *git_oid_shorten_new(size_t min_length)
-{
- git_oid_shorten *os;
-
- assert((size_t)((int)min_length) == min_length);
-
- os = git__calloc(1, sizeof(git_oid_shorten));
- if (os == NULL)
- return NULL;
-
- if (resize_trie(os, 16) < 0) {
- git__free(os);
- return NULL;
- }
-
- os->node_count = 1;
- os->min_length = (int)min_length;
-
- return os;
-}
-
-void git_oid_shorten_free(git_oid_shorten *os)
-{
- if (os == NULL)
- return;
-
- git__free(os->nodes);
- git__free(os);
-}
-
-
-/*
- * What wizardry is this?
- *
- * This is just a memory-optimized trie: basically a very fancy
- * 16-ary tree, which is used to store the prefixes of the OID
- * strings.
- *
- * Read more: http://en.wikipedia.org/wiki/Trie
- *
- * Magic that happens in this method:
- *
- * - Each node in the trie is an union, so it can work both as
- * a normal node, or as a leaf.
- *
- * - Each normal node points to 16 children (one for each possible
- * character in the oid). This is *not* stored in an array of
- * pointers, because in a 64-bit arch this would be sucking
- * 16*sizeof(void*) = 128 bytes of memory per node, which is
- * insane. What we do is store Node Indexes, and use these indexes
- * to look up each node in the om->index array. These indexes are
- * signed shorts, so this limits the amount of unique OIDs that
- * fit in the structure to about 20000 (assuming a more or less uniform
- * distribution).
- *
- * - All the nodes in om->index array are stored contiguously in
- * memory, and each of them is 32 bytes, so we fit 2x nodes per
- * cache line. Convenient for speed.
- *
- * - To differentiate the leafs from the normal nodes, we store all
- * the indexes towards a leaf as a negative index (indexes to normal
- * nodes are positives). When we find that one of the children for
- * a node has a negative value, that means it's going to be a leaf.
- * This reduces the amount of indexes we have by two, but also reduces
- * the size of each node by 1-4 bytes (the amount we would need to
- * add a `is_leaf` field): this is good because it allows the nodes
- * to fit cleanly in cache lines.
- *
- * - Once we reach an empty children, instead of continuing to insert
- * new nodes for each remaining character of the OID, we store a pointer
- * to the tail in the leaf; if the leaf is reached again, we turn it
- * into a normal node and use the tail to create a new leaf.
- *
- * This is a pretty good balance between performance and memory usage.
- */
-int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)
-{
- int i;
- bool is_leaf;
- node_index idx;
-
- if (os->full) {
- giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
- return -1;
- }
-
- if (text_oid == NULL)
- return os->min_length;
-
- idx = 0;
- is_leaf = false;
-
- for (i = 0; i < GIT_OID_HEXSZ; ++i) {
- int c = git__fromhex(text_oid[i]);
- trie_node *node;
-
- if (c == -1) {
- giterr_set(GITERR_INVALID, "Unable to shorten OID - invalid hex value");
- return -1;
- }
-
- node = &os->nodes[idx];
-
- if (is_leaf) {
- const char *tail;
-
- tail = node->tail;
- node->tail = NULL;
-
- node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]);
- if (node == NULL) {
- if (os->full)
- giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
- return -1;
- }
- }
-
- if (node->children[c] == 0) {
- if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL) {
- if (os->full)
- giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
- return -1;
- }
- break;
- }
-
- idx = node->children[c];
- is_leaf = false;
-
- if (idx < 0) {
- node->children[c] = idx = -idx;
- is_leaf = true;
- }
- }
-
- if (++i > os->min_length)
- os->min_length = i;
-
- return os->min_length;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_oid_h__
-#define INCLUDE_oid_h__
-
-#include "git2/oid.h"
-
-/**
- * Format a git_oid into a newly allocated c-string.
- *
- * The c-string is owned by the caller and needs to be manually freed.
- *
- * @param id the oid structure to format
- * @return the c-string; NULL if memory is exhausted. Caller must
- * deallocate the string with git__free().
- */
-char *git_oid_allocfmt(const git_oid *id);
-
-GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2)
-{
- int i;
-
- for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
- if (*sha1 != *sha2)
- return *sha1 - *sha2;
- }
-
- return 0;
-}
-
-/*
- * Compare two oid structures.
- *
- * @param a first oid structure.
- * @param b second oid structure.
- * @return <0, 0, >0 if a < b, a == b, a > b.
- */
-GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
-{
- return git_oid__hashcmp(a->id, b->id);
-}
-
-GIT_INLINE(void) git_oid__cpy_prefix(
- git_oid *out, const git_oid *id, size_t len)
-{
- memcpy(&out->id, id->id, (len + 1) / 2);
-
- if (len & 1)
- out->id[len / 2] &= 0xF0;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/oidarray.h"
-#include "oidarray.h"
-#include "array.h"
-
-void git_oidarray_free(git_oidarray *arr)
-{
- git__free(arr->ids);
-}
-
-void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array)
-{
- arr->count = array->size;
- arr->ids = array->ptr;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_oidarray_h__
-#define INCLUDE_oidarray_h__
-
-#include "common.h"
-#include "git2/oidarray.h"
-#include "array.h"
-
-typedef git_array_t(git_oid) git_array_oid_t;
-
-extern void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_oidmap_h__
-#define INCLUDE_oidmap_h__
-
-#include "common.h"
-#include "git2/oid.h"
-
-#define kmalloc git__malloc
-#define kcalloc git__calloc
-#define krealloc git__realloc
-#define kreallocarray git__reallocarray
-#define kfree git__free
-#include "khash.h"
-
-__KHASH_TYPE(oid, const git_oid *, void *)
-typedef khash_t(oid) git_oidmap;
-
-GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid)
-{
- khint_t h;
- memcpy(&h, oid, sizeof(khint_t));
- return h;
-}
-
-#define GIT__USE_OIDMAP \
- __KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal)
-
-#define git_oidmap_alloc() kh_init(oid)
-#define git_oidmap_free(h) kh_destroy(oid,h), h = NULL
-
-#define git_oidmap_lookup_index(h, k) kh_get(oid, h, k)
-#define git_oidmap_valid_index(h, idx) (idx != kh_end(h))
-
-#define git_oidmap_value_at(h, idx) kh_val(h, idx)
-
-#define git_oidmap_insert(h, key, val, rval) do { \
- khiter_t __pos = kh_put(oid, h, key, &rval); \
- if (rval >= 0) { \
- if (rval == 0) kh_key(h, __pos) = key; \
- kh_val(h, __pos) = val; \
- } } while (0)
-
-#define git_oidmap_foreach_value kh_foreach_value
-
-#define git_oidmap_size(h) kh_size(h)
-
-#define git_oidmap_clear(h) kh_clear(oid, h)
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifdef GIT_OPENSSL
-
-#include <ctype.h>
-
-#include "global.h"
-#include "posix.h"
-#include "stream.h"
-#include "socket_stream.h"
-#include "openssl_stream.h"
-#include "netops.h"
-#include "git2/transport.h"
-#include "git2/sys/openssl.h"
-
-#ifdef GIT_CURL
-# include "curl_stream.h"
-#endif
-
-#ifndef GIT_WIN32
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-#endif
-
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/x509v3.h>
-#include <openssl/bio.h>
-
-SSL_CTX *git__ssl_ctx;
-
-#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
-
-#ifdef GIT_THREADS
-
-static git_mutex *openssl_locks;
-
-static void openssl_locking_function(
- int mode, int n, const char *file, int line)
-{
- int lock;
-
- GIT_UNUSED(file);
- GIT_UNUSED(line);
-
- lock = mode & CRYPTO_LOCK;
-
- if (lock) {
- git_mutex_lock(&openssl_locks[n]);
- } else {
- git_mutex_unlock(&openssl_locks[n]);
- }
-}
-
-static void shutdown_ssl_locking(void)
-{
- int num_locks, i;
-
- num_locks = CRYPTO_num_locks();
- CRYPTO_set_locking_callback(NULL);
-
- for (i = 0; i < num_locks; ++i)
- git_mutex_free(openssl_locks);
- git__free(openssl_locks);
-}
-
-#endif /* GIT_THREADS */
-
-static BIO_METHOD *git_stream_bio_method;
-static int init_bio_method(void);
-
-/**
- * This function aims to clean-up the SSL context which
- * we allocated.
- */
-static void shutdown_ssl(void)
-{
- if (git_stream_bio_method) {
- BIO_meth_free(git_stream_bio_method);
- git_stream_bio_method = NULL;
- }
-
- if (git__ssl_ctx) {
- SSL_CTX_free(git__ssl_ctx);
- git__ssl_ctx = NULL;
- }
-}
-
-int git_openssl_stream_global_init(void)
-{
-#ifdef GIT_OPENSSL
- long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
- const char *ciphers = git_libgit2__ssl_ciphers();
-
- /* Older OpenSSL and MacOS OpenSSL doesn't have this */
-#ifdef SSL_OP_NO_COMPRESSION
- ssl_opts |= SSL_OP_NO_COMPRESSION;
-#endif
-
- SSL_load_error_strings();
- OpenSSL_add_ssl_algorithms();
- /*
- * Load SSLv{2,3} and TLSv1 so that we can talk with servers
- * which use the SSL hellos, which are often used for
- * compatibility. We then disable SSL so we only allow OpenSSL
- * to speak TLSv1 to perform the encryption itself.
- */
- git__ssl_ctx = SSL_CTX_new(SSLv23_method());
- SSL_CTX_set_options(git__ssl_ctx, ssl_opts);
- SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY);
- SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL);
- if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) {
- SSL_CTX_free(git__ssl_ctx);
- git__ssl_ctx = NULL;
- return -1;
- }
-
- if (!ciphers) {
- ciphers = GIT_SSL_DEFAULT_CIPHERS;
- }
-
- if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
- SSL_CTX_free(git__ssl_ctx);
- git__ssl_ctx = NULL;
- return -1;
- }
-
- if (init_bio_method() < 0) {
- SSL_CTX_free(git__ssl_ctx);
- git__ssl_ctx = NULL;
- return -1;
- }
-
-#endif
-
- git__on_shutdown(shutdown_ssl);
-
- return 0;
-}
-
-int git_openssl_set_locking(void)
-{
-#ifdef GIT_THREADS
- int num_locks, i;
-
- num_locks = CRYPTO_num_locks();
- openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
- GITERR_CHECK_ALLOC(openssl_locks);
-
- for (i = 0; i < num_locks; i++) {
- if (git_mutex_init(&openssl_locks[i]) != 0) {
- giterr_set(GITERR_SSL, "failed to initialize openssl locks");
- return -1;
- }
- }
-
- CRYPTO_set_locking_callback(openssl_locking_function);
- git__on_shutdown(shutdown_ssl_locking);
- return 0;
-#else
- giterr_set(GITERR_THREAD, "libgit2 was not built with threads");
- return -1;
-#endif
-}
-
-
-static int bio_create(BIO *b)
-{
- BIO_set_init(b, 1);
- BIO_set_data(b, NULL);
-
- return 1;
-}
-
-static int bio_destroy(BIO *b)
-{
- if (!b)
- return 0;
-
- BIO_set_data(b, NULL);
-
- return 1;
-}
-
-static int bio_read(BIO *b, char *buf, int len)
-{
- git_stream *io = (git_stream *) BIO_get_data(b);
-
- return (int) git_stream_read(io, buf, len);
-}
-
-static int bio_write(BIO *b, const char *buf, int len)
-{
- git_stream *io = (git_stream *) BIO_get_data(b);
-
- return (int) git_stream_write(io, buf, len, 0);
-}
-
-static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
-{
- GIT_UNUSED(b);
- GIT_UNUSED(num);
- GIT_UNUSED(ptr);
-
- if (cmd == BIO_CTRL_FLUSH)
- return 1;
-
- return 0;
-}
-
-static int bio_gets(BIO *b, char *buf, int len)
-{
- GIT_UNUSED(b);
- GIT_UNUSED(buf);
- GIT_UNUSED(len);
- return -1;
-}
-
-static int bio_puts(BIO *b, const char *str)
-{
- return bio_write(b, str, strlen(str));
-}
-
-static int init_bio_method(void)
-{
- /* Set up the BIO_METHOD we use for wrapping our own stream implementations */
- git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream");
- GITERR_CHECK_ALLOC(git_stream_bio_method);
-
- BIO_meth_set_write(git_stream_bio_method, bio_write);
- BIO_meth_set_read(git_stream_bio_method, bio_read);
- BIO_meth_set_puts(git_stream_bio_method, bio_puts);
- BIO_meth_set_gets(git_stream_bio_method, bio_gets);
- BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl);
- BIO_meth_set_create(git_stream_bio_method, bio_create);
- BIO_meth_set_destroy(git_stream_bio_method, bio_destroy);
-
- return 0;
-}
-
-static int ssl_set_error(SSL *ssl, int error)
-{
- int err;
- unsigned long e;
-
- err = SSL_get_error(ssl, error);
-
- assert(err != SSL_ERROR_WANT_READ);
- assert(err != SSL_ERROR_WANT_WRITE);
-
- switch (err) {
- case SSL_ERROR_WANT_CONNECT:
- case SSL_ERROR_WANT_ACCEPT:
- giterr_set(GITERR_NET, "SSL error: connection failure\n");
- break;
- case SSL_ERROR_WANT_X509_LOOKUP:
- giterr_set(GITERR_NET, "SSL error: x509 error\n");
- break;
- case SSL_ERROR_SYSCALL:
- e = ERR_get_error();
- if (e > 0) {
- giterr_set(GITERR_NET, "SSL error: %s",
- ERR_error_string(e, NULL));
- break;
- } else if (error < 0) {
- giterr_set(GITERR_OS, "SSL error: syscall failure");
- break;
- }
- giterr_set(GITERR_NET, "SSL error: received early EOF");
- return GIT_EEOF;
- break;
- case SSL_ERROR_SSL:
- e = ERR_get_error();
- giterr_set(GITERR_NET, "SSL error: %s",
- ERR_error_string(e, NULL));
- break;
- case SSL_ERROR_NONE:
- case SSL_ERROR_ZERO_RETURN:
- default:
- giterr_set(GITERR_NET, "SSL error: unknown error");
- break;
- }
- return -1;
-}
-
-static int ssl_teardown(SSL *ssl)
-{
- int ret;
-
- ret = SSL_shutdown(ssl);
- if (ret < 0)
- ret = ssl_set_error(ssl, ret);
- else
- ret = 0;
-
- return ret;
-}
-
-static int check_host_name(const char *name, const char *host)
-{
- if (!strcasecmp(name, host))
- return 0;
-
- if (gitno__match_host(name, host) < 0)
- return -1;
-
- return 0;
-}
-
-static int verify_server_cert(SSL *ssl, const char *host)
-{
- X509 *cert;
- X509_NAME *peer_name;
- ASN1_STRING *str;
- unsigned char *peer_cn = NULL;
- int matched = -1, type = GEN_DNS;
- GENERAL_NAMES *alts;
- struct in6_addr addr6;
- struct in_addr addr4;
- void *addr;
- int i = -1,j;
-
- if (SSL_get_verify_result(ssl) != X509_V_OK) {
- giterr_set(GITERR_SSL, "The SSL certificate is invalid");
- return GIT_ECERTIFICATE;
- }
-
- /* Try to parse the host as an IP address to see if it is */
- if (p_inet_pton(AF_INET, host, &addr4)) {
- type = GEN_IPADD;
- addr = &addr4;
- } else {
- if(p_inet_pton(AF_INET6, host, &addr6)) {
- type = GEN_IPADD;
- addr = &addr6;
- }
- }
-
-
- cert = SSL_get_peer_certificate(ssl);
- if (!cert) {
- giterr_set(GITERR_SSL, "the server did not provide a certificate");
- return -1;
- }
-
- /* Check the alternative names */
- alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
- if (alts) {
- int num;
-
- num = sk_GENERAL_NAME_num(alts);
- for (i = 0; i < num && matched != 1; i++) {
- const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
- const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5);
- size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
-
- /* Skip any names of a type we're not looking for */
- if (gn->type != type)
- continue;
-
- if (type == GEN_DNS) {
- /* If it contains embedded NULs, don't even try */
- if (memchr(name, '\0', namelen))
- continue;
-
- if (check_host_name(name, host) < 0)
- matched = 0;
- else
- matched = 1;
- } else if (type == GEN_IPADD) {
- /* Here name isn't so much a name but a binary representation of the IP */
- matched = !!memcmp(name, addr, namelen);
- }
- }
- }
- GENERAL_NAMES_free(alts);
-
- if (matched == 0)
- goto cert_fail_name;
-
- if (matched == 1)
- return 0;
-
- /* If no alternative names are available, check the common name */
- peer_name = X509_get_subject_name(cert);
- if (peer_name == NULL)
- goto on_error;
-
- if (peer_name) {
- /* Get the index of the last CN entry */
- while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
- i = j;
- }
-
- if (i < 0)
- goto on_error;
-
- str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
- if (str == NULL)
- goto on_error;
-
- /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
- if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
- int size = ASN1_STRING_length(str);
-
- if (size > 0) {
- peer_cn = OPENSSL_malloc(size + 1);
- GITERR_CHECK_ALLOC(peer_cn);
- memcpy(peer_cn, ASN1_STRING_get0_data(str), size);
- peer_cn[size] = '\0';
- } else {
- goto cert_fail_name;
- }
- } else {
- int size = ASN1_STRING_to_UTF8(&peer_cn, str);
- GITERR_CHECK_ALLOC(peer_cn);
- if (memchr(peer_cn, '\0', size))
- goto cert_fail_name;
- }
-
- if (check_host_name((char *)peer_cn, host) < 0)
- goto cert_fail_name;
-
- OPENSSL_free(peer_cn);
-
- return 0;
-
-on_error:
- OPENSSL_free(peer_cn);
- return ssl_set_error(ssl, 0);
-
-cert_fail_name:
- OPENSSL_free(peer_cn);
- giterr_set(GITERR_SSL, "hostname does not match certificate");
- return GIT_ECERTIFICATE;
-}
-
-typedef struct {
- git_stream parent;
- git_stream *io;
- bool connected;
- char *host;
- SSL *ssl;
- git_cert_x509 cert_info;
-} openssl_stream;
-
-int openssl_close(git_stream *stream);
-
-int openssl_connect(git_stream *stream)
-{
- int ret;
- BIO *bio;
- openssl_stream *st = (openssl_stream *) stream;
-
- if ((ret = git_stream_connect(st->io)) < 0)
- return ret;
-
- st->connected = true;
-
- bio = BIO_new(git_stream_bio_method);
- GITERR_CHECK_ALLOC(bio);
-
- BIO_set_data(bio, st->io);
- SSL_set_bio(st->ssl, bio, bio);
-
- /* specify the host in case SNI is needed */
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
- SSL_set_tlsext_host_name(st->ssl, st->host);
-#endif
-
- if ((ret = SSL_connect(st->ssl)) <= 0)
- return ssl_set_error(st->ssl, ret);
-
- return verify_server_cert(st->ssl, st->host);
-}
-
-int openssl_certificate(git_cert **out, git_stream *stream)
-{
- openssl_stream *st = (openssl_stream *) stream;
- int len;
- X509 *cert = SSL_get_peer_certificate(st->ssl);
- unsigned char *guard, *encoded_cert;
-
- /* Retrieve the length of the certificate first */
- len = i2d_X509(cert, NULL);
- if (len < 0) {
- giterr_set(GITERR_NET, "failed to retrieve certificate information");
- return -1;
- }
-
- encoded_cert = git__malloc(len);
- GITERR_CHECK_ALLOC(encoded_cert);
- /* i2d_X509 makes 'guard' point to just after the data */
- guard = encoded_cert;
-
- len = i2d_X509(cert, &guard);
- if (len < 0) {
- git__free(encoded_cert);
- giterr_set(GITERR_NET, "failed to retrieve certificate information");
- return -1;
- }
-
- st->cert_info.parent.cert_type = GIT_CERT_X509;
- st->cert_info.data = encoded_cert;
- st->cert_info.len = len;
-
- *out = &st->cert_info.parent;
-
- return 0;
-}
-
-static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
-{
- openssl_stream *st = (openssl_stream *) stream;
-
- return git_stream_set_proxy(st->io, proxy_opts);
-}
-
-ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
-{
- openssl_stream *st = (openssl_stream *) stream;
- int ret;
-
- GIT_UNUSED(flags);
-
- if ((ret = SSL_write(st->ssl, data, len)) <= 0) {
- return ssl_set_error(st->ssl, ret);
- }
-
- return ret;
-}
-
-ssize_t openssl_read(git_stream *stream, void *data, size_t len)
-{
- openssl_stream *st = (openssl_stream *) stream;
- int ret;
-
- if ((ret = SSL_read(st->ssl, data, len)) <= 0)
- return ssl_set_error(st->ssl, ret);
-
- return ret;
-}
-
-int openssl_close(git_stream *stream)
-{
- openssl_stream *st = (openssl_stream *) stream;
- int ret;
-
- if (st->connected && (ret = ssl_teardown(st->ssl)) < 0)
- return -1;
-
- st->connected = false;
-
- return git_stream_close(st->io);
-}
-
-void openssl_free(git_stream *stream)
-{
- openssl_stream *st = (openssl_stream *) stream;
-
- SSL_free(st->ssl);
- git__free(st->host);
- git__free(st->cert_info.data);
- git_stream_free(st->io);
- git__free(st);
-}
-
-int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
-{
- int error;
- openssl_stream *st;
-
- st = git__calloc(1, sizeof(openssl_stream));
- GITERR_CHECK_ALLOC(st);
-
- st->io = NULL;
-#ifdef GIT_CURL
- error = git_curl_stream_new(&st->io, host, port);
-#else
- error = git_socket_stream_new(&st->io, host, port);
-#endif
-
- if (error < 0)
- goto out_err;
-
- st->ssl = SSL_new(git__ssl_ctx);
- if (st->ssl == NULL) {
- giterr_set(GITERR_SSL, "failed to create ssl object");
- error = -1;
- goto out_err;
- }
-
- st->host = git__strdup(host);
- GITERR_CHECK_ALLOC(st->host);
-
- st->parent.version = GIT_STREAM_VERSION;
- st->parent.encrypted = 1;
- st->parent.proxy_support = git_stream_supports_proxy(st->io);
- st->parent.connect = openssl_connect;
- st->parent.certificate = openssl_certificate;
- st->parent.set_proxy = openssl_set_proxy;
- st->parent.read = openssl_read;
- st->parent.write = openssl_write;
- st->parent.close = openssl_close;
- st->parent.free = openssl_free;
-
- *out = (git_stream *) st;
- return 0;
-
-out_err:
- git_stream_free(st->io);
- git__free(st);
-
- return error;
-}
-
-#else
-
-#include "stream.h"
-#include "git2/sys/openssl.h"
-
-int git_openssl_stream_global_init(void)
-{
- return 0;
-}
-
-int git_openssl_set_locking(void)
-{
- giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
- return -1;
-}
-
-int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
-{
- GIT_UNUSED(out);
- GIT_UNUSED(host);
- GIT_UNUSED(port);
-
- giterr_set(GITERR_SSL, "openssl is not supported in this version");
- return -1;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_openssl_stream_h__
-#define INCLUDE_openssl_stream_h__
-
-#include "git2/sys/stream.h"
-
-extern int git_openssl_stream_global_init(void);
-
-extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
-
-/*
- * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
- * which do not exist in previous versions. We define these inline functions so
- * we can program against the interface instead of littering the implementation
- * with ifdefs.
- */
-#ifdef GIT_OPENSSL
-# include <openssl/ssl.h>
-# include <openssl/err.h>
-# include <openssl/x509v3.h>
-# include <openssl/bio.h>
-
-
-
-# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
-
-GIT_INLINE(BIO_METHOD*) BIO_meth_new(int type, const char *name)
-{
- BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
- if (!meth) {
- return NULL;
- }
-
- meth->type = type;
- meth->name = name;
-
- return meth;
-}
-
-GIT_INLINE(void) BIO_meth_free(BIO_METHOD *biom)
-{
- git__free(biom);
-}
-
-GIT_INLINE(int) BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
-{
- biom->bwrite = write;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
-{
- biom->bread = read;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
-{
- biom->bputs = puts;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
-
-{
- biom->bgets = gets;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
-{
- biom->ctrl = ctrl;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
-{
- biom->create = create;
- return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
-{
- biom->destroy = destroy;
- return 1;
-}
-
-GIT_INLINE(int) BIO_get_new_index(void)
-{
- /* This exists as of 1.1 so before we'd just have 0 */
- return 0;
-}
-
-GIT_INLINE(void) BIO_set_init(BIO *b, int init)
-{
- b->init = init;
-}
-
-GIT_INLINE(void) BIO_set_data(BIO *a, void *ptr)
-{
- a->ptr = ptr;
-}
-
-GIT_INLINE(void*) BIO_get_data(BIO *a)
-{
- return a->ptr;
-}
-
-GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x)
-{
- return ASN1_STRING_data((ASN1_STRING *)x);
-}
-
-# endif // OpenSSL < 1.1
-#endif // GIT_OPENSSL
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "pack-objects.h"
-
-#include "zstream.h"
-#include "delta.h"
-#include "iterator.h"
-#include "netops.h"
-#include "pack.h"
-#include "thread-utils.h"
-#include "tree.h"
-#include "util.h"
-#include "revwalk.h"
-#include "commit_list.h"
-
-#include "git2/pack.h"
-#include "git2/commit.h"
-#include "git2/tag.h"
-#include "git2/indexer.h"
-#include "git2/config.h"
-
-struct unpacked {
- git_pobject *object;
- void *data;
- struct git_delta_index *index;
- size_t depth;
-};
-
-struct tree_walk_context {
- git_packbuilder *pb;
- git_buf buf;
-};
-
-struct pack_write_context {
- git_indexer *indexer;
- git_transfer_progress *stats;
-};
-
-GIT__USE_OIDMAP
-
-#ifdef GIT_THREADS
-
-#define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) do { \
- int result = git_mutex_##op(&(pb)->mtx); \
- assert(!result); \
- GIT_UNUSED(result); \
- } while (0)
-
-#else
-
-#define GIT_PACKBUILDER__MUTEX_OP(pb,mtx,op) GIT_UNUSED(pb)
-
-#endif /* GIT_THREADS */
-
-#define git_packbuilder__cache_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, lock)
-#define git_packbuilder__cache_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, unlock)
-#define git_packbuilder__progress_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, lock)
-#define git_packbuilder__progress_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, unlock)
-
-/* The minimal interval between progress updates (in seconds). */
-#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
-
-/* Size of the buffer to feed to zlib */
-#define COMPRESS_BUFLEN (1024 * 1024)
-
-static unsigned name_hash(const char *name)
-{
- unsigned c, hash = 0;
-
- if (!name)
- return 0;
-
- /*
- * This effectively just creates a sortable number from the
- * last sixteen non-whitespace characters. Last characters
- * count "most", so things that end in ".c" sort together.
- */
- while ((c = *name++) != 0) {
- if (git__isspace(c))
- continue;
- hash = (hash >> 2) + (c << 24);
- }
- return hash;
-}
-
-static int packbuilder_config(git_packbuilder *pb)
-{
- git_config *config;
- int ret = 0;
- int64_t val;
-
- if ((ret = git_repository_config_snapshot(&config, pb->repo)) < 0)
- return ret;
-
-#define config_get(KEY,DST,DFLT) do { \
- ret = git_config_get_int64(&val, config, KEY); \
- if (!ret) { \
- if (!git__is_sizet(val)) { \
- giterr_set(GITERR_CONFIG, \
- "configuration value '%s' is too large", KEY); \
- ret = -1; \
- goto out; \
- } \
- (DST) = (size_t)val; \
- } else if (ret == GIT_ENOTFOUND) { \
- (DST) = (DFLT); \
- ret = 0; \
- } else if (ret < 0) goto out; } while (0)
-
- config_get("pack.deltaCacheSize", pb->max_delta_cache_size,
- GIT_PACK_DELTA_CACHE_SIZE);
- config_get("pack.deltaCacheLimit", pb->cache_max_small_delta_size,
- GIT_PACK_DELTA_CACHE_LIMIT);
- config_get("pack.deltaCacheSize", pb->big_file_threshold,
- GIT_PACK_BIG_FILE_THRESHOLD);
- config_get("pack.windowMemory", pb->window_memory_limit, 0);
-
-#undef config_get
-
-out:
- git_config_free(config);
-
- return ret;
-}
-
-int git_packbuilder_new(git_packbuilder **out, git_repository *repo)
-{
- git_packbuilder *pb;
-
- *out = NULL;
-
- pb = git__calloc(1, sizeof(*pb));
- GITERR_CHECK_ALLOC(pb);
-
- pb->object_ix = git_oidmap_alloc();
- if (!pb->object_ix)
- goto on_error;
-
- pb->walk_objects = git_oidmap_alloc();
- if (!pb->walk_objects)
- goto on_error;
-
- git_pool_init(&pb->object_pool, sizeof(git_walk_object));
-
- pb->repo = repo;
- pb->nr_threads = 1; /* do not spawn any thread by default */
-
- if (git_hash_ctx_init(&pb->ctx) < 0 ||
- git_zstream_init(&pb->zstream, GIT_ZSTREAM_DEFLATE) < 0 ||
- git_repository_odb(&pb->odb, repo) < 0 ||
- packbuilder_config(pb) < 0)
- goto on_error;
-
-#ifdef GIT_THREADS
-
- if (git_mutex_init(&pb->cache_mutex) ||
- git_mutex_init(&pb->progress_mutex) ||
- git_cond_init(&pb->progress_cond))
- {
- giterr_set(GITERR_OS, "Failed to initialize packbuilder mutex");
- goto on_error;
- }
-
-#endif
-
- *out = pb;
- return 0;
-
-on_error:
- git_packbuilder_free(pb);
- return -1;
-}
-
-unsigned int git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n)
-{
- assert(pb);
-
-#ifdef GIT_THREADS
- pb->nr_threads = n;
-#else
- GIT_UNUSED(n);
- assert(1 == pb->nr_threads);
-#endif
-
- return pb->nr_threads;
-}
-
-static void rehash(git_packbuilder *pb)
-{
- git_pobject *po;
- khiter_t pos;
- size_t i;
- int ret;
-
- kh_clear(oid, pb->object_ix);
- for (i = 0, po = pb->object_list; i < pb->nr_objects; i++, po++) {
- pos = kh_put(oid, pb->object_ix, &po->id, &ret);
- kh_value(pb->object_ix, pos) = po;
- }
-}
-
-int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
- const char *name)
-{
- git_pobject *po;
- khiter_t pos;
- size_t newsize;
- int ret;
-
- assert(pb && oid);
-
- /* If the object already exists in the hash table, then we don't
- * have any work to do */
- pos = kh_get(oid, pb->object_ix, oid);
- if (pos != kh_end(pb->object_ix))
- return 0;
-
- if (pb->nr_objects >= pb->nr_alloc) {
- GITERR_CHECK_ALLOC_ADD(&newsize, pb->nr_alloc, 1024);
- GITERR_CHECK_ALLOC_MULTIPLY(&newsize, newsize, 3 / 2);
-
- if (!git__is_uint32(newsize)) {
- giterr_set(GITERR_NOMEMORY, "Packfile too large to fit in memory.");
- return -1;
- }
-
- pb->nr_alloc = newsize;
-
- pb->object_list = git__reallocarray(pb->object_list,
- pb->nr_alloc, sizeof(*po));
- GITERR_CHECK_ALLOC(pb->object_list);
- rehash(pb);
- }
-
- po = pb->object_list + pb->nr_objects;
- memset(po, 0x0, sizeof(*po));
-
- if ((ret = git_odb_read_header(&po->size, &po->type, pb->odb, oid)) < 0)
- return ret;
-
- pb->nr_objects++;
- git_oid_cpy(&po->id, oid);
- po->hash = name_hash(name);
-
- pos = kh_put(oid, pb->object_ix, &po->id, &ret);
- if (ret < 0) {
- giterr_set_oom();
- return ret;
- }
- assert(ret != 0);
- kh_value(pb->object_ix, pos) = po;
-
- pb->done = false;
-
- if (pb->progress_cb) {
- double current_time = git__timer();
- double elapsed = current_time - pb->last_progress_report_time;
-
- if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
- pb->last_progress_report_time = current_time;
-
- ret = pb->progress_cb(
- GIT_PACKBUILDER_ADDING_OBJECTS,
- pb->nr_objects, 0, pb->progress_cb_payload);
-
- if (ret)
- return giterr_set_after_callback(ret);
- }
- }
-
- return 0;
-}
-
-static int get_delta(void **out, git_odb *odb, git_pobject *po)
-{
- git_odb_object *src = NULL, *trg = NULL;
- size_t delta_size;
- void *delta_buf;
- int error;
-
- *out = NULL;
-
- if (git_odb_read(&src, odb, &po->delta->id) < 0 ||
- git_odb_read(&trg, odb, &po->id) < 0)
- goto on_error;
-
- error = git_delta(&delta_buf, &delta_size,
- git_odb_object_data(src), git_odb_object_size(src),
- git_odb_object_data(trg), git_odb_object_size(trg),
- 0);
-
- if (error < 0 && error != GIT_EBUFS)
- goto on_error;
-
- if (error == GIT_EBUFS || delta_size != po->delta_size) {
- giterr_set(GITERR_INVALID, "Delta size changed");
- goto on_error;
- }
-
- *out = delta_buf;
-
- git_odb_object_free(src);
- git_odb_object_free(trg);
- return 0;
-
-on_error:
- git_odb_object_free(src);
- git_odb_object_free(trg);
- return -1;
-}
-
-static int write_object(
- git_packbuilder *pb,
- git_pobject *po,
- int (*write_cb)(void *buf, size_t size, void *cb_data),
- void *cb_data)
-{
- git_odb_object *obj = NULL;
- git_otype type;
- unsigned char hdr[10], *zbuf = NULL;
- void *data = NULL;
- size_t hdr_len, zbuf_len = COMPRESS_BUFLEN, data_len;
- int error;
-
- /*
- * If we have a delta base, let's use the delta to save space.
- * Otherwise load the whole object. 'data' ends up pointing to
- * whatever data we want to put into the packfile.
- */
- if (po->delta) {
- if (po->delta_data)
- data = po->delta_data;
- else if ((error = get_delta(&data, pb->odb, po)) < 0)
- goto done;
-
- data_len = po->delta_size;
- type = GIT_OBJ_REF_DELTA;
- } else {
- if ((error = git_odb_read(&obj, pb->odb, &po->id)) < 0)
- goto done;
-
- data = (void *)git_odb_object_data(obj);
- data_len = git_odb_object_size(obj);
- type = git_odb_object_type(obj);
- }
-
- /* Write header */
- hdr_len = git_packfile__object_header(hdr, data_len, type);
-
- if ((error = write_cb(hdr, hdr_len, cb_data)) < 0 ||
- (error = git_hash_update(&pb->ctx, hdr, hdr_len)) < 0)
- goto done;
-
- if (type == GIT_OBJ_REF_DELTA) {
- if ((error = write_cb(po->delta->id.id, GIT_OID_RAWSZ, cb_data)) < 0 ||
- (error = git_hash_update(&pb->ctx, po->delta->id.id, GIT_OID_RAWSZ)) < 0)
- goto done;
- }
-
- /* Write data */
- if (po->z_delta_size) {
- data_len = po->z_delta_size;
-
- if ((error = write_cb(data, data_len, cb_data)) < 0 ||
- (error = git_hash_update(&pb->ctx, data, data_len)) < 0)
- goto done;
- } else {
- zbuf = git__malloc(zbuf_len);
- GITERR_CHECK_ALLOC(zbuf);
-
- git_zstream_reset(&pb->zstream);
- git_zstream_set_input(&pb->zstream, data, data_len);
-
- while (!git_zstream_done(&pb->zstream)) {
- if ((error = git_zstream_get_output(zbuf, &zbuf_len, &pb->zstream)) < 0 ||
- (error = write_cb(zbuf, zbuf_len, cb_data)) < 0 ||
- (error = git_hash_update(&pb->ctx, zbuf, zbuf_len)) < 0)
- goto done;
-
- zbuf_len = COMPRESS_BUFLEN; /* reuse buffer */
- }
- }
-
- /*
- * If po->delta is true, data is a delta and it is our
- * responsibility to free it (otherwise it's a git_object's
- * data). We set po->delta_data to NULL in case we got the
- * data from there instead of get_delta(). If we didn't,
- * there's no harm.
- */
- if (po->delta) {
- git__free(data);
- po->delta_data = NULL;
- }
-
- pb->nr_written++;
-
-done:
- git__free(zbuf);
- git_odb_object_free(obj);
- return error;
-}
-
-enum write_one_status {
- WRITE_ONE_SKIP = -1, /* already written */
- WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */
- WRITE_ONE_WRITTEN = 1, /* normal */
- WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
-};
-
-static int write_one(
- enum write_one_status *status,
- git_packbuilder *pb,
- git_pobject *po,
- int (*write_cb)(void *buf, size_t size, void *cb_data),
- void *cb_data)
-{
- int error;
-
- if (po->recursing) {
- *status = WRITE_ONE_RECURSIVE;
- return 0;
- } else if (po->written) {
- *status = WRITE_ONE_SKIP;
- return 0;
- }
-
- if (po->delta) {
- po->recursing = 1;
-
- if ((error = write_one(status, pb, po->delta, write_cb, cb_data)) < 0)
- return error;
-
- /* we cannot depend on this one */
- if (*status == WRITE_ONE_RECURSIVE)
- po->delta = NULL;
- }
-
- *status = WRITE_ONE_WRITTEN;
- po->written = 1;
- po->recursing = 0;
-
- return write_object(pb, po, write_cb, cb_data);
-}
-
-GIT_INLINE(void) add_to_write_order(git_pobject **wo, size_t *endp,
- git_pobject *po)
-{
- if (po->filled)
- return;
- wo[(*endp)++] = po;
- po->filled = 1;
-}
-
-static void add_descendants_to_write_order(git_pobject **wo, size_t *endp,
- git_pobject *po)
-{
- int add_to_order = 1;
- while (po) {
- if (add_to_order) {
- git_pobject *s;
- /* add this node... */
- add_to_write_order(wo, endp, po);
- /* all its siblings... */
- for (s = po->delta_sibling; s; s = s->delta_sibling) {
- add_to_write_order(wo, endp, s);
- }
- }
- /* drop down a level to add left subtree nodes if possible */
- if (po->delta_child) {
- add_to_order = 1;
- po = po->delta_child;
- } else {
- add_to_order = 0;
- /* our sibling might have some children, it is next */
- if (po->delta_sibling) {
- po = po->delta_sibling;
- continue;
- }
- /* go back to our parent node */
- po = po->delta;
- while (po && !po->delta_sibling) {
- /* we're on the right side of a subtree, keep
- * going up until we can go right again */
- po = po->delta;
- }
- if (!po) {
- /* done- we hit our original root node */
- return;
- }
- /* pass it off to sibling at this level */
- po = po->delta_sibling;
- }
- };
-}
-
-static void add_family_to_write_order(git_pobject **wo, size_t *endp,
- git_pobject *po)
-{
- git_pobject *root;
-
- for (root = po; root->delta; root = root->delta)
- ; /* nothing */
- add_descendants_to_write_order(wo, endp, root);
-}
-
-static int cb_tag_foreach(const char *name, git_oid *oid, void *data)
-{
- git_packbuilder *pb = data;
- git_pobject *po;
- khiter_t pos;
-
- GIT_UNUSED(name);
-
- pos = kh_get(oid, pb->object_ix, oid);
- if (pos == kh_end(pb->object_ix))
- return 0;
-
- po = kh_value(pb->object_ix, pos);
- po->tagged = 1;
-
- /* TODO: peel objects */
-
- return 0;
-}
-
-static git_pobject **compute_write_order(git_packbuilder *pb)
-{
- size_t i, wo_end, last_untagged;
- git_pobject **wo;
-
- if ((wo = git__mallocarray(pb->nr_objects, sizeof(*wo))) == NULL)
- return NULL;
-
- for (i = 0; i < pb->nr_objects; i++) {
- git_pobject *po = pb->object_list + i;
- po->tagged = 0;
- po->filled = 0;
- po->delta_child = NULL;
- po->delta_sibling = NULL;
- }
-
- /*
- * Fully connect delta_child/delta_sibling network.
- * Make sure delta_sibling is sorted in the original
- * recency order.
- */
- for (i = pb->nr_objects; i > 0;) {
- git_pobject *po = &pb->object_list[--i];
- if (!po->delta)
- continue;
- /* Mark me as the first child */
- po->delta_sibling = po->delta->delta_child;
- po->delta->delta_child = po;
- }
-
- /*
- * Mark objects that are at the tip of tags.
- */
- if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0) {
- git__free(wo);
- return NULL;
- }
-
- /*
- * Give the objects in the original recency order until
- * we see a tagged tip.
- */
- for (i = wo_end = 0; i < pb->nr_objects; i++) {
- git_pobject *po = pb->object_list + i;
- if (po->tagged)
- break;
- add_to_write_order(wo, &wo_end, po);
- }
- last_untagged = i;
-
- /*
- * Then fill all the tagged tips.
- */
- for (; i < pb->nr_objects; i++) {
- git_pobject *po = pb->object_list + i;
- if (po->tagged)
- add_to_write_order(wo, &wo_end, po);
- }
-
- /*
- * And then all remaining commits and tags.
- */
- for (i = last_untagged; i < pb->nr_objects; i++) {
- git_pobject *po = pb->object_list + i;
- if (po->type != GIT_OBJ_COMMIT &&
- po->type != GIT_OBJ_TAG)
- continue;
- add_to_write_order(wo, &wo_end, po);
- }
-
- /*
- * And then all the trees.
- */
- for (i = last_untagged; i < pb->nr_objects; i++) {
- git_pobject *po = pb->object_list + i;
- if (po->type != GIT_OBJ_TREE)
- continue;
- add_to_write_order(wo, &wo_end, po);
- }
-
- /*
- * Finally all the rest in really tight order
- */
- for (i = last_untagged; i < pb->nr_objects; i++) {
- git_pobject *po = pb->object_list + i;
- if (!po->filled)
- add_family_to_write_order(wo, &wo_end, po);
- }
-
- if (wo_end != pb->nr_objects) {
- git__free(wo);
- giterr_set(GITERR_INVALID, "invalid write order");
- return NULL;
- }
-
- return wo;
-}
-
-static int write_pack(git_packbuilder *pb,
- int (*write_cb)(void *buf, size_t size, void *cb_data),
- void *cb_data)
-{
- git_pobject **write_order;
- git_pobject *po;
- enum write_one_status status;
- struct git_pack_header ph;
- git_oid entry_oid;
- size_t i = 0;
- int error = 0;
-
- write_order = compute_write_order(pb);
- if (write_order == NULL)
- return -1;
-
- if (!git__is_uint32(pb->nr_objects)) {
- giterr_set(GITERR_INVALID, "too many objects");
- return -1;
- }
-
- /* Write pack header */
- ph.hdr_signature = htonl(PACK_SIGNATURE);
- ph.hdr_version = htonl(PACK_VERSION);
- ph.hdr_entries = htonl(pb->nr_objects);
-
- if ((error = write_cb(&ph, sizeof(ph), cb_data)) < 0 ||
- (error = git_hash_update(&pb->ctx, &ph, sizeof(ph))) < 0)
- goto done;
-
- pb->nr_remaining = pb->nr_objects;
- do {
- pb->nr_written = 0;
- for ( ; i < pb->nr_objects; ++i) {
- po = write_order[i];
-
- if ((error = write_one(&status, pb, po, write_cb, cb_data)) < 0)
- goto done;
- }
-
- pb->nr_remaining -= pb->nr_written;
- } while (pb->nr_remaining && i < pb->nr_objects);
-
- if ((error = git_hash_final(&entry_oid, &pb->ctx)) < 0)
- goto done;
-
- error = write_cb(entry_oid.id, GIT_OID_RAWSZ, cb_data);
-
-done:
- /* if callback cancelled writing, we must still free delta_data */
- for ( ; i < pb->nr_objects; ++i) {
- po = write_order[i];
- if (po->delta_data) {
- git__free(po->delta_data);
- po->delta_data = NULL;
- }
- }
-
- git__free(write_order);
- return error;
-}
-
-static int write_pack_buf(void *buf, size_t size, void *data)
-{
- git_buf *b = (git_buf *)data;
- return git_buf_put(b, buf, size);
-}
-
-static int type_size_sort(const void *_a, const void *_b)
-{
- const git_pobject *a = (git_pobject *)_a;
- const git_pobject *b = (git_pobject *)_b;
-
- if (a->type > b->type)
- return -1;
- if (a->type < b->type)
- return 1;
- if (a->hash > b->hash)
- return -1;
- if (a->hash < b->hash)
- return 1;
- /*
- * TODO
- *
- if (a->preferred_base > b->preferred_base)
- return -1;
- if (a->preferred_base < b->preferred_base)
- return 1;
- */
- if (a->size > b->size)
- return -1;
- if (a->size < b->size)
- return 1;
- return a < b ? -1 : (a > b); /* newest first */
-}
-
-static int delta_cacheable(
- git_packbuilder *pb,
- size_t src_size,
- size_t trg_size,
- size_t delta_size)
-{
- size_t new_size;
-
- if (git__add_sizet_overflow(&new_size, pb->delta_cache_size, delta_size))
- return 0;
-
- if (pb->max_delta_cache_size && new_size > pb->max_delta_cache_size)
- return 0;
-
- if (delta_size < pb->cache_max_small_delta_size)
- return 1;
-
- /* cache delta, if objects are large enough compared to delta size */
- if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10))
- return 1;
-
- return 0;
-}
-
-static int try_delta(git_packbuilder *pb, struct unpacked *trg,
- struct unpacked *src, size_t max_depth,
- size_t *mem_usage, int *ret)
-{
- git_pobject *trg_object = trg->object;
- git_pobject *src_object = src->object;
- git_odb_object *obj;
- size_t trg_size, src_size, delta_size, sizediff, max_size, sz;
- size_t ref_depth;
- void *delta_buf;
-
- /* Don't bother doing diffs between different types */
- if (trg_object->type != src_object->type) {
- *ret = -1;
- return 0;
- }
-
- *ret = 0;
-
- /* TODO: support reuse-delta */
-
- /* Let's not bust the allowed depth. */
- if (src->depth >= max_depth)
- return 0;
-
- /* Now some size filtering heuristics. */
- trg_size = trg_object->size;
- if (!trg_object->delta) {
- max_size = trg_size/2 - 20;
- ref_depth = 1;
- } else {
- max_size = trg_object->delta_size;
- ref_depth = trg->depth;
- }
-
- max_size = (uint64_t)max_size * (max_depth - src->depth) /
- (max_depth - ref_depth + 1);
- if (max_size == 0)
- return 0;
-
- src_size = src_object->size;
- sizediff = src_size < trg_size ? trg_size - src_size : 0;
- if (sizediff >= max_size)
- return 0;
- if (trg_size < src_size / 32)
- return 0;
-
- /* Load data if not already done */
- if (!trg->data) {
- if (git_odb_read(&obj, pb->odb, &trg_object->id) < 0)
- return -1;
-
- sz = git_odb_object_size(obj);
- trg->data = git__malloc(sz);
- GITERR_CHECK_ALLOC(trg->data);
- memcpy(trg->data, git_odb_object_data(obj), sz);
-
- git_odb_object_free(obj);
-
- if (sz != trg_size) {
- giterr_set(GITERR_INVALID,
- "Inconsistent target object length");
- return -1;
- }
-
- *mem_usage += sz;
- }
- if (!src->data) {
- size_t obj_sz;
-
- if (git_odb_read(&obj, pb->odb, &src_object->id) < 0 ||
- !git__is_ulong(obj_sz = git_odb_object_size(obj)))
- return -1;
-
- sz = obj_sz;
- src->data = git__malloc(sz);
- GITERR_CHECK_ALLOC(src->data);
- memcpy(src->data, git_odb_object_data(obj), sz);
-
- git_odb_object_free(obj);
-
- if (sz != src_size) {
- giterr_set(GITERR_INVALID,
- "Inconsistent source object length");
- return -1;
- }
-
- *mem_usage += sz;
- }
- if (!src->index) {
- if (git_delta_index_init(&src->index, src->data, src_size) < 0)
- return 0; /* suboptimal pack - out of memory */
-
- *mem_usage += git_delta_index_size(src->index);
- }
-
- if (git_delta_create_from_index(&delta_buf, &delta_size, src->index, trg->data, trg_size,
- max_size) < 0)
- return 0;
-
- if (trg_object->delta) {
- /* Prefer only shallower same-sized deltas. */
- if (delta_size == trg_object->delta_size &&
- src->depth + 1 >= trg->depth) {
- git__free(delta_buf);
- return 0;
- }
- }
-
- git_packbuilder__cache_lock(pb);
- if (trg_object->delta_data) {
- git__free(trg_object->delta_data);
- assert(pb->delta_cache_size >= trg_object->delta_size);
- pb->delta_cache_size -= trg_object->delta_size;
- trg_object->delta_data = NULL;
- }
- if (delta_cacheable(pb, src_size, trg_size, delta_size)) {
- bool overflow = git__add_sizet_overflow(
- &pb->delta_cache_size, pb->delta_cache_size, delta_size);
-
- git_packbuilder__cache_unlock(pb);
-
- if (overflow) {
- git__free(delta_buf);
- return -1;
- }
-
- trg_object->delta_data = git__realloc(delta_buf, delta_size);
- GITERR_CHECK_ALLOC(trg_object->delta_data);
- } else {
- /* create delta when writing the pack */
- git_packbuilder__cache_unlock(pb);
- git__free(delta_buf);
- }
-
- trg_object->delta = src_object;
- trg_object->delta_size = delta_size;
- trg->depth = src->depth + 1;
-
- *ret = 1;
- return 0;
-}
-
-static size_t check_delta_limit(git_pobject *me, size_t n)
-{
- git_pobject *child = me->delta_child;
- size_t m = n;
-
- while (child) {
- size_t c = check_delta_limit(child, n + 1);
- if (m < c)
- m = c;
- child = child->delta_sibling;
- }
- return m;
-}
-
-static size_t free_unpacked(struct unpacked *n)
-{
- size_t freed_mem = 0;
-
- if (n->index) {
- freed_mem += git_delta_index_size(n->index);
- git_delta_index_free(n->index);
- }
- n->index = NULL;
-
- if (n->data) {
- freed_mem += n->object->size;
- git__free(n->data);
- n->data = NULL;
- }
- n->object = NULL;
- n->depth = 0;
- return freed_mem;
-}
-
-static int report_delta_progress(
- git_packbuilder *pb, uint32_t count, bool force)
-{
- int ret;
-
- if (pb->progress_cb) {
- double current_time = git__timer();
- double elapsed = current_time - pb->last_progress_report_time;
-
- if (force || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
- pb->last_progress_report_time = current_time;
-
- ret = pb->progress_cb(
- GIT_PACKBUILDER_DELTAFICATION,
- count, pb->nr_objects, pb->progress_cb_payload);
-
- if (ret)
- return giterr_set_after_callback(ret);
- }
- }
-
- return 0;
-}
-
-static int find_deltas(git_packbuilder *pb, git_pobject **list,
- size_t *list_size, size_t window, size_t depth)
-{
- git_pobject *po;
- git_buf zbuf = GIT_BUF_INIT;
- struct unpacked *array;
- size_t idx = 0, count = 0;
- size_t mem_usage = 0;
- size_t i;
- int error = -1;
-
- array = git__calloc(window, sizeof(struct unpacked));
- GITERR_CHECK_ALLOC(array);
-
- for (;;) {
- struct unpacked *n = array + idx;
- size_t max_depth, j, best_base = SIZE_MAX;
-
- git_packbuilder__progress_lock(pb);
- if (!*list_size) {
- git_packbuilder__progress_unlock(pb);
- break;
- }
-
- pb->nr_deltified += 1;
- report_delta_progress(pb, pb->nr_deltified, false);
-
- po = *list++;
- (*list_size)--;
- git_packbuilder__progress_unlock(pb);
-
- mem_usage -= free_unpacked(n);
- n->object = po;
-
- while (pb->window_memory_limit &&
- mem_usage > pb->window_memory_limit &&
- count > 1) {
- size_t tail = (idx + window - count) % window;
- mem_usage -= free_unpacked(array + tail);
- count--;
- }
-
- /*
- * If the current object is at pack edge, take the depth the
- * objects that depend on the current object into account
- * otherwise they would become too deep.
- */
- max_depth = depth;
- if (po->delta_child) {
- size_t delta_limit = check_delta_limit(po, 0);
-
- if (delta_limit > max_depth)
- goto next;
-
- max_depth -= delta_limit;
- }
-
- j = window;
- while (--j > 0) {
- int ret;
- size_t other_idx = idx + j;
- struct unpacked *m;
-
- if (other_idx >= window)
- other_idx -= window;
-
- m = array + other_idx;
- if (!m->object)
- break;
-
- if (try_delta(pb, n, m, max_depth, &mem_usage, &ret) < 0)
- goto on_error;
- if (ret < 0)
- break;
- else if (ret > 0)
- best_base = other_idx;
- }
-
- /*
- * If we decided to cache the delta data, then it is best
- * to compress it right away. First because we have to do
- * it anyway, and doing it here while we're threaded will
- * save a lot of time in the non threaded write phase,
- * as well as allow for caching more deltas within
- * the same cache size limit.
- * ...
- * But only if not writing to stdout, since in that case
- * the network is most likely throttling writes anyway,
- * and therefore it is best to go to the write phase ASAP
- * instead, as we can afford spending more time compressing
- * between writes at that moment.
- */
- if (po->delta_data) {
- if (git_zstream_deflatebuf(&zbuf, po->delta_data, po->delta_size) < 0)
- goto on_error;
-
- git__free(po->delta_data);
- po->delta_data = git__malloc(zbuf.size);
- GITERR_CHECK_ALLOC(po->delta_data);
-
- memcpy(po->delta_data, zbuf.ptr, zbuf.size);
- po->z_delta_size = zbuf.size;
- git_buf_clear(&zbuf);
-
- git_packbuilder__cache_lock(pb);
- pb->delta_cache_size -= po->delta_size;
- pb->delta_cache_size += po->z_delta_size;
- git_packbuilder__cache_unlock(pb);
- }
-
- /*
- * If we made n a delta, and if n is already at max
- * depth, leaving it in the window is pointless. we
- * should evict it first.
- */
- if (po->delta && max_depth <= n->depth)
- continue;
-
- /*
- * Move the best delta base up in the window, after the
- * currently deltified object, to keep it longer. It will
- * be the first base object to be attempted next.
- */
- if (po->delta) {
- struct unpacked swap = array[best_base];
- size_t dist = (window + idx - best_base) % window;
- size_t dst = best_base;
- while (dist--) {
- size_t src = (dst + 1) % window;
- array[dst] = array[src];
- dst = src;
- }
- array[dst] = swap;
- }
-
- next:
- idx++;
- if (count + 1 < window)
- count++;
- if (idx >= window)
- idx = 0;
- }
- error = 0;
-
-on_error:
- for (i = 0; i < window; ++i) {
- git__free(array[i].index);
- git__free(array[i].data);
- }
- git__free(array);
- git_buf_free(&zbuf);
-
- return error;
-}
-
-#ifdef GIT_THREADS
-
-struct thread_params {
- git_thread thread;
- git_packbuilder *pb;
-
- git_pobject **list;
-
- git_cond cond;
- git_mutex mutex;
-
- size_t list_size;
- size_t remaining;
-
- size_t window;
- size_t depth;
- size_t working;
- size_t data_ready;
-};
-
-static void *threaded_find_deltas(void *arg)
-{
- struct thread_params *me = arg;
-
- while (me->remaining) {
- if (find_deltas(me->pb, me->list, &me->remaining,
- me->window, me->depth) < 0) {
- ; /* TODO */
- }
-
- git_packbuilder__progress_lock(me->pb);
- me->working = 0;
- git_cond_signal(&me->pb->progress_cond);
- git_packbuilder__progress_unlock(me->pb);
-
- if (git_mutex_lock(&me->mutex)) {
- giterr_set(GITERR_THREAD, "unable to lock packfile condition mutex");
- return NULL;
- }
-
- while (!me->data_ready)
- git_cond_wait(&me->cond, &me->mutex);
-
- /*
- * We must not set ->data_ready before we wait on the
- * condition because the main thread may have set it to 1
- * before we get here. In order to be sure that new
- * work is available if we see 1 in ->data_ready, it
- * was initialized to 0 before this thread was spawned
- * and we reset it to 0 right away.
- */
- me->data_ready = 0;
- git_mutex_unlock(&me->mutex);
- }
- /* leave ->working 1 so that this doesn't get more work assigned */
- return NULL;
-}
-
-static int ll_find_deltas(git_packbuilder *pb, git_pobject **list,
- size_t list_size, size_t window, size_t depth)
-{
- struct thread_params *p;
- size_t i;
- int ret, active_threads = 0;
-
- if (!pb->nr_threads)
- pb->nr_threads = git_online_cpus();
-
- if (pb->nr_threads <= 1) {
- find_deltas(pb, list, &list_size, window, depth);
- return 0;
- }
-
- p = git__mallocarray(pb->nr_threads, sizeof(*p));
- GITERR_CHECK_ALLOC(p);
-
- /* Partition the work among the threads */
- for (i = 0; i < pb->nr_threads; ++i) {
- size_t sub_size = list_size / (pb->nr_threads - i);
-
- /* don't use too small segments or no deltas will be found */
- if (sub_size < 2*window && i+1 < pb->nr_threads)
- sub_size = 0;
-
- p[i].pb = pb;
- p[i].window = window;
- p[i].depth = depth;
- p[i].working = 1;
- p[i].data_ready = 0;
-
- /* try to split chunks on "path" boundaries */
- while (sub_size && sub_size < list_size &&
- list[sub_size]->hash &&
- list[sub_size]->hash == list[sub_size-1]->hash)
- sub_size++;
-
- p[i].list = list;
- p[i].list_size = sub_size;
- p[i].remaining = sub_size;
-
- list += sub_size;
- list_size -= sub_size;
- }
-
- /* Start work threads */
- for (i = 0; i < pb->nr_threads; ++i) {
- if (!p[i].list_size)
- continue;
-
- git_mutex_init(&p[i].mutex);
- git_cond_init(&p[i].cond);
-
- ret = git_thread_create(&p[i].thread,
- threaded_find_deltas, &p[i]);
- if (ret) {
- giterr_set(GITERR_THREAD, "unable to create thread");
- return -1;
- }
- active_threads++;
- }
-
- /*
- * Now let's wait for work completion. Each time a thread is done
- * with its work, we steal half of the remaining work from the
- * thread with the largest number of unprocessed objects and give
- * it to that newly idle thread. This ensure good load balancing
- * until the remaining object list segments are simply too short
- * to be worth splitting anymore.
- */
- while (active_threads) {
- struct thread_params *target = NULL;
- struct thread_params *victim = NULL;
- size_t sub_size = 0;
-
- /* Start by locating a thread that has transitioned its
- * 'working' flag from 1 -> 0. This indicates that it is
- * ready to receive more work using our work-stealing
- * algorithm. */
- git_packbuilder__progress_lock(pb);
- for (;;) {
- for (i = 0; !target && i < pb->nr_threads; i++)
- if (!p[i].working)
- target = &p[i];
- if (target)
- break;
- git_cond_wait(&pb->progress_cond, &pb->progress_mutex);
- }
-
- /* At this point we hold the progress lock and have located
- * a thread to receive more work. We still need to locate a
- * thread from which to steal work (the victim). */
- for (i = 0; i < pb->nr_threads; i++)
- if (p[i].remaining > 2*window &&
- (!victim || victim->remaining < p[i].remaining))
- victim = &p[i];
-
- if (victim) {
- sub_size = victim->remaining / 2;
- list = victim->list + victim->list_size - sub_size;
- while (sub_size && list[0]->hash &&
- list[0]->hash == list[-1]->hash) {
- list++;
- sub_size--;
- }
- if (!sub_size) {
- /*
- * It is possible for some "paths" to have
- * so many objects that no hash boundary
- * might be found. Let's just steal the
- * exact half in that case.
- */
- sub_size = victim->remaining / 2;
- list -= sub_size;
- }
- target->list = list;
- victim->list_size -= sub_size;
- victim->remaining -= sub_size;
- }
- target->list_size = sub_size;
- target->remaining = sub_size;
- target->working = 1;
- git_packbuilder__progress_unlock(pb);
-
- if (git_mutex_lock(&target->mutex)) {
- giterr_set(GITERR_THREAD, "unable to lock packfile condition mutex");
- git__free(p);
- return -1;
- }
-
- target->data_ready = 1;
- git_cond_signal(&target->cond);
- git_mutex_unlock(&target->mutex);
-
- if (!sub_size) {
- git_thread_join(&target->thread, NULL);
- git_cond_free(&target->cond);
- git_mutex_free(&target->mutex);
- active_threads--;
- }
- }
-
- git__free(p);
- return 0;
-}
-
-#else
-#define ll_find_deltas(pb, l, ls, w, d) find_deltas(pb, l, &ls, w, d)
-#endif
-
-static int prepare_pack(git_packbuilder *pb)
-{
- git_pobject **delta_list;
- size_t i, n = 0;
-
- if (pb->nr_objects == 0 || pb->done)
- return 0; /* nothing to do */
-
- /*
- * Although we do not report progress during deltafication, we
- * at least report that we are in the deltafication stage
- */
- if (pb->progress_cb)
- pb->progress_cb(GIT_PACKBUILDER_DELTAFICATION, 0, pb->nr_objects, pb->progress_cb_payload);
-
- delta_list = git__mallocarray(pb->nr_objects, sizeof(*delta_list));
- GITERR_CHECK_ALLOC(delta_list);
-
- for (i = 0; i < pb->nr_objects; ++i) {
- git_pobject *po = pb->object_list + i;
-
- /* Make sure the item is within our size limits */
- if (po->size < 50 || po->size > pb->big_file_threshold)
- continue;
-
- delta_list[n++] = po;
- }
-
- if (n > 1) {
- git__tsort((void **)delta_list, n, type_size_sort);
- if (ll_find_deltas(pb, delta_list, n,
- GIT_PACK_WINDOW + 1,
- GIT_PACK_DEPTH) < 0) {
- git__free(delta_list);
- return -1;
- }
- }
-
- report_delta_progress(pb, pb->nr_objects, true);
-
- pb->done = true;
- git__free(delta_list);
- return 0;
-}
-
-#define PREPARE_PACK if (prepare_pack(pb) < 0) { return -1; }
-
-int git_packbuilder_foreach(git_packbuilder *pb, int (*cb)(void *buf, size_t size, void *payload), void *payload)
-{
- PREPARE_PACK;
- return write_pack(pb, cb, payload);
-}
-
-int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb)
-{
- PREPARE_PACK;
- git_buf_sanitize(buf);
- return write_pack(pb, &write_pack_buf, buf);
-}
-
-static int write_cb(void *buf, size_t len, void *payload)
-{
- struct pack_write_context *ctx = payload;
- return git_indexer_append(ctx->indexer, buf, len, ctx->stats);
-}
-
-int git_packbuilder_write(
- git_packbuilder *pb,
- const char *path,
- unsigned int mode,
- git_transfer_progress_cb progress_cb,
- void *progress_cb_payload)
-{
- git_indexer *indexer;
- git_transfer_progress stats;
- struct pack_write_context ctx;
-
- PREPARE_PACK;
-
- if (git_indexer_new(
- &indexer, path, mode, pb->odb, progress_cb, progress_cb_payload) < 0)
- return -1;
-
- ctx.indexer = indexer;
- ctx.stats = &stats;
-
- if (git_packbuilder_foreach(pb, write_cb, &ctx) < 0 ||
- git_indexer_commit(indexer, &stats) < 0) {
- git_indexer_free(indexer);
- return -1;
- }
-
- git_oid_cpy(&pb->pack_oid, git_indexer_hash(indexer));
-
- git_indexer_free(indexer);
- return 0;
-}
-
-#undef PREPARE_PACK
-
-const git_oid *git_packbuilder_hash(git_packbuilder *pb)
-{
- return &pb->pack_oid;
-}
-
-
-static int cb_tree_walk(
- const char *root, const git_tree_entry *entry, void *payload)
-{
- int error;
- struct tree_walk_context *ctx = payload;
-
- /* A commit inside a tree represents a submodule commit and should be skipped. */
- if (git_tree_entry_type(entry) == GIT_OBJ_COMMIT)
- return 0;
-
- if (!(error = git_buf_sets(&ctx->buf, root)) &&
- !(error = git_buf_puts(&ctx->buf, git_tree_entry_name(entry))))
- error = git_packbuilder_insert(
- ctx->pb, git_tree_entry_id(entry), git_buf_cstr(&ctx->buf));
-
- return error;
-}
-
-int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid)
-{
- git_commit *commit;
-
- if (git_commit_lookup(&commit, pb->repo, oid) < 0 ||
- git_packbuilder_insert(pb, oid, NULL) < 0)
- return -1;
-
- if (git_packbuilder_insert_tree(pb, git_commit_tree_id(commit)) < 0)
- return -1;
-
- git_commit_free(commit);
- return 0;
-}
-
-int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid)
-{
- int error;
- git_tree *tree = NULL;
- struct tree_walk_context context = { pb, GIT_BUF_INIT };
-
- if (!(error = git_tree_lookup(&tree, pb->repo, oid)) &&
- !(error = git_packbuilder_insert(pb, oid, NULL)))
- error = git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context);
-
- git_tree_free(tree);
- git_buf_free(&context.buf);
- return error;
-}
-
-int git_packbuilder_insert_recur(git_packbuilder *pb, const git_oid *id, const char *name)
-{
- git_object *obj;
- int error;
-
- assert(pb && id);
-
- if ((error = git_object_lookup(&obj, pb->repo, id, GIT_OBJ_ANY)) < 0)
- return error;
-
- switch (git_object_type(obj)) {
- case GIT_OBJ_BLOB:
- error = git_packbuilder_insert(pb, id, name);
- break;
- case GIT_OBJ_TREE:
- error = git_packbuilder_insert_tree(pb, id);
- break;
- case GIT_OBJ_COMMIT:
- error = git_packbuilder_insert_commit(pb, id);
- break;
- case GIT_OBJ_TAG:
- if ((error = git_packbuilder_insert(pb, id, name)) < 0)
- goto cleanup;
- error = git_packbuilder_insert_recur(pb, git_tag_target_id((git_tag *) obj), NULL);
- break;
-
- default:
- giterr_set(GITERR_INVALID, "unknown object type");
- error = -1;
- }
-
-cleanup:
- git_object_free(obj);
- return error;
-}
-
-size_t git_packbuilder_object_count(git_packbuilder *pb)
-{
- return pb->nr_objects;
-}
-
-size_t git_packbuilder_written(git_packbuilder *pb)
-{
- return pb->nr_written;
-}
-
-int lookup_walk_object(git_walk_object **out, git_packbuilder *pb, const git_oid *id)
-{
- git_walk_object *obj;
-
- obj = git_pool_mallocz(&pb->object_pool, 1);
- if (!obj) {
- giterr_set_oom();
- return -1;
- }
-
- git_oid_cpy(&obj->id, id);
-
- *out = obj;
- return 0;
-}
-
-static int retrieve_object(git_walk_object **out, git_packbuilder *pb, const git_oid *id)
-{
- int error;
- khiter_t pos;
- git_walk_object *obj;
-
- pos = git_oidmap_lookup_index(pb->walk_objects, id);
- if (git_oidmap_valid_index(pb->walk_objects, pos)) {
- obj = git_oidmap_value_at(pb->walk_objects, pos);
- } else {
- if ((error = lookup_walk_object(&obj, pb, id)) < 0)
- return error;
-
- git_oidmap_insert(pb->walk_objects, &obj->id, obj, error);
- }
-
- *out = obj;
- return 0;
-}
-
-static int mark_blob_uninteresting(git_packbuilder *pb, const git_oid *id)
-{
- int error;
- git_walk_object *obj;
-
- if ((error = retrieve_object(&obj, pb, id)) < 0)
- return error;
-
- obj->uninteresting = 1;
-
- return 0;
-}
-
-static int mark_tree_uninteresting(git_packbuilder *pb, const git_oid *id)
-{
- git_walk_object *obj;
- git_tree *tree;
- int error;
- size_t i;
-
- if ((error = retrieve_object(&obj, pb, id)) < 0)
- return error;
-
- if (obj->uninteresting)
- return 0;
-
- obj->uninteresting = 1;
-
- if ((error = git_tree_lookup(&tree, pb->repo, id)) < 0)
- return error;
-
- for (i = 0; i < git_tree_entrycount(tree); i++) {
- const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
- const git_oid *entry_id = git_tree_entry_id(entry);
- switch (git_tree_entry_type(entry)) {
- case GIT_OBJ_TREE:
- if ((error = mark_tree_uninteresting(pb, entry_id)) < 0)
- goto cleanup;
- break;
- case GIT_OBJ_BLOB:
- if ((error = mark_blob_uninteresting(pb, entry_id)) < 0)
- goto cleanup;
- break;
- default:
- /* it's a submodule or something unknown, we don't want it */
- ;
- }
- }
-
-cleanup:
- git_tree_free(tree);
- return error;
-}
-
-/*
- * Mark the edges of the graph uninteresting. Since we start from a
- * git_revwalk, the commits are already uninteresting, but we need to
- * mark the trees and blobs.
- */
-static int mark_edges_uninteresting(git_packbuilder *pb, git_commit_list *commits)
-{
- int error;
- git_commit_list *list;
- git_commit *commit;
-
- for (list = commits; list; list = list->next) {
- if (!list->item->uninteresting)
- continue;
-
- if ((error = git_commit_lookup(&commit, pb->repo, &list->item->oid)) < 0)
- return error;
-
- error = mark_tree_uninteresting(pb, git_commit_tree_id(commit));
- git_commit_free(commit);
-
- if (error < 0)
- return error;
- }
-
- return 0;
-}
-
-int insert_tree(git_packbuilder *pb, git_tree *tree)
-{
- size_t i;
- int error;
- git_tree *subtree;
- git_walk_object *obj;
- const char *name;
-
- if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0)
- return error;
-
- if (obj->seen)
- return 0;
-
- obj->seen = 1;
-
- if ((error = git_packbuilder_insert(pb, &obj->id, NULL)))
- return error;
-
- for (i = 0; i < git_tree_entrycount(tree); i++) {
- const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
- const git_oid *entry_id = git_tree_entry_id(entry);
- switch (git_tree_entry_type(entry)) {
- case GIT_OBJ_TREE:
- if ((error = git_tree_lookup(&subtree, pb->repo, entry_id)) < 0)
- return error;
-
- error = insert_tree(pb, subtree);
- git_tree_free(subtree);
-
- if (error < 0)
- return error;
-
- break;
- case GIT_OBJ_BLOB:
- name = git_tree_entry_name(entry);
- if ((error = git_packbuilder_insert(pb, entry_id, name)) < 0)
- return error;
- break;
- default:
- /* it's a submodule or something unknown, we don't want it */
- ;
- }
- }
-
-
- return error;
-}
-
-int insert_commit(git_packbuilder *pb, git_walk_object *obj)
-{
- int error;
- git_commit *commit = NULL;
- git_tree *tree = NULL;
-
- obj->seen = 1;
-
- if ((error = git_packbuilder_insert(pb, &obj->id, NULL)) < 0)
- return error;
-
- if ((error = git_commit_lookup(&commit, pb->repo, &obj->id)) < 0)
- return error;
-
- if ((error = git_tree_lookup(&tree, pb->repo, git_commit_tree_id(commit))) < 0)
- goto cleanup;
-
- if ((error = insert_tree(pb, tree)) < 0)
- goto cleanup;
-
-cleanup:
- git_commit_free(commit);
- git_tree_free(tree);
- return error;
-}
-
-int git_packbuilder_insert_walk(git_packbuilder *pb, git_revwalk *walk)
-{
- int error;
- git_oid id;
- git_walk_object *obj;
-
- assert(pb && walk);
-
- if ((error = mark_edges_uninteresting(pb, walk->user_input)) < 0)
- return error;
-
- /*
- * TODO: git marks the parents of the edges
- * uninteresting. This may provide a speed advantage, but does
- * seem to assume the remote does not have a single-commit
- * history on the other end.
- */
-
- /* walk down each tree up to the blobs and insert them, stopping when uninteresting */
- while ((error = git_revwalk_next(&id, walk)) == 0) {
- if ((error = retrieve_object(&obj, pb, &id)) < 0)
- return error;
-
- if (obj->seen || obj->uninteresting)
- continue;
-
- if ((error = insert_commit(pb, obj)) < 0)
- return error;
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- return 0;
-}
-
-int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload)
-{
- if (!pb)
- return -1;
-
- pb->progress_cb = progress_cb;
- pb->progress_cb_payload = progress_cb_payload;
-
- return 0;
-}
-
-void git_packbuilder_free(git_packbuilder *pb)
-{
- if (pb == NULL)
- return;
-
-#ifdef GIT_THREADS
-
- git_mutex_free(&pb->cache_mutex);
- git_mutex_free(&pb->progress_mutex);
- git_cond_free(&pb->progress_cond);
-
-#endif
-
- if (pb->odb)
- git_odb_free(pb->odb);
-
- if (pb->object_ix)
- git_oidmap_free(pb->object_ix);
-
- if (pb->object_list)
- git__free(pb->object_list);
-
- git_oidmap_free(pb->walk_objects);
- git_pool_clear(&pb->object_pool);
-
- git_hash_ctx_cleanup(&pb->ctx);
- git_zstream_free(&pb->zstream);
-
- git__free(pb);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_pack_objects_h__
-#define INCLUDE_pack_objects_h__
-
-#include "common.h"
-
-#include "buffer.h"
-#include "hash.h"
-#include "oidmap.h"
-#include "netops.h"
-#include "zstream.h"
-#include "pool.h"
-
-#include "git2/oid.h"
-#include "git2/pack.h"
-
-#define GIT_PACK_WINDOW 10 /* number of objects to possibly delta against */
-#define GIT_PACK_DEPTH 50 /* max delta depth */
-#define GIT_PACK_DELTA_CACHE_SIZE (256 * 1024 * 1024)
-#define GIT_PACK_DELTA_CACHE_LIMIT 1000
-#define GIT_PACK_BIG_FILE_THRESHOLD (512 * 1024 * 1024)
-
-typedef struct git_pobject {
- git_oid id;
- git_otype type;
- git_off_t offset;
-
- size_t size;
-
- unsigned int hash; /* name hint hash */
-
- struct git_pobject *delta; /* delta base object */
- struct git_pobject *delta_child; /* deltified objects who bases me */
- struct git_pobject *delta_sibling; /* other deltified objects
- * who uses the same base as
- * me */
-
- void *delta_data;
- size_t delta_size;
- size_t z_delta_size;
-
- int written:1,
- recursing:1,
- tagged:1,
- filled:1;
-} git_pobject;
-
-typedef struct {
- git_oid id;
- unsigned int uninteresting:1,
- seen:1;
-} git_walk_object;
-
-struct git_packbuilder {
- git_repository *repo; /* associated repository */
- git_odb *odb; /* associated object database */
-
- git_hash_ctx ctx;
- git_zstream zstream;
-
- uint32_t nr_objects,
- nr_deltified,
- nr_written,
- nr_remaining;
-
- size_t nr_alloc;
-
- git_pobject *object_list;
-
- git_oidmap *object_ix;
-
- git_oidmap *walk_objects;
- git_pool object_pool;
-
- git_oid pack_oid; /* hash of written pack */
-
- /* synchronization objects */
- git_mutex cache_mutex;
- git_mutex progress_mutex;
- git_cond progress_cond;
-
- /* configs */
- size_t delta_cache_size;
- size_t max_delta_cache_size;
- size_t cache_max_small_delta_size;
- size_t big_file_threshold;
- size_t window_memory_limit;
-
- unsigned int nr_threads; /* nr of threads to use */
-
- git_packbuilder_progress progress_cb;
- void *progress_cb_payload;
- double last_progress_report_time; /* the time progress was last reported */
-
- bool done;
-};
-
-int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb);
-
-#endif /* INCLUDE_pack_objects_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "odb.h"
-#include "pack.h"
-#include "delta.h"
-#include "sha1_lookup.h"
-#include "mwindow.h"
-#include "fileops.h"
-#include "oid.h"
-
-#include <zlib.h>
-
-GIT__USE_OFFMAP
-GIT__USE_OIDMAP
-
-static int packfile_open(struct git_pack_file *p);
-static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n);
-static int packfile_unpack_compressed(
- git_rawobj *obj,
- struct git_pack_file *p,
- git_mwindow **w_curs,
- git_off_t *curpos,
- size_t size,
- git_otype type);
-
-/* Can find the offset of an object given
- * a prefix of an identifier.
- * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid
- * is ambiguous within the pack.
- * This method assumes that len is between
- * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
- */
-static int pack_entry_find_offset(
- git_off_t *offset_out,
- git_oid *found_oid,
- struct git_pack_file *p,
- const git_oid *short_oid,
- size_t len);
-
-static int packfile_error(const char *message)
-{
- giterr_set(GITERR_ODB, "Invalid pack file - %s", message);
- return -1;
-}
-
-/********************
- * Delta base cache
- ********************/
-
-static git_pack_cache_entry *new_cache_object(git_rawobj *source)
-{
- git_pack_cache_entry *e = git__calloc(1, sizeof(git_pack_cache_entry));
- if (!e)
- return NULL;
-
- git_atomic_inc(&e->refcount);
- memcpy(&e->raw, source, sizeof(git_rawobj));
-
- return e;
-}
-
-static void free_cache_object(void *o)
-{
- git_pack_cache_entry *e = (git_pack_cache_entry *)o;
-
- if (e != NULL) {
- assert(e->refcount.val == 0);
- git__free(e->raw.data);
- git__free(e);
- }
-}
-
-static void cache_free(git_pack_cache *cache)
-{
- khiter_t k;
-
- if (cache->entries) {
- for (k = kh_begin(cache->entries); k != kh_end(cache->entries); k++) {
- if (kh_exist(cache->entries, k))
- free_cache_object(kh_value(cache->entries, k));
- }
-
- git_offmap_free(cache->entries);
- cache->entries = NULL;
- }
-}
-
-static int cache_init(git_pack_cache *cache)
-{
- cache->entries = git_offmap_alloc();
- GITERR_CHECK_ALLOC(cache->entries);
-
- cache->memory_limit = GIT_PACK_CACHE_MEMORY_LIMIT;
-
- if (git_mutex_init(&cache->lock)) {
- giterr_set(GITERR_OS, "Failed to initialize pack cache mutex");
-
- git__free(cache->entries);
- cache->entries = NULL;
-
- return -1;
- }
-
- return 0;
-}
-
-static git_pack_cache_entry *cache_get(git_pack_cache *cache, git_off_t offset)
-{
- khiter_t k;
- git_pack_cache_entry *entry = NULL;
-
- if (git_mutex_lock(&cache->lock) < 0)
- return NULL;
-
- k = kh_get(off, cache->entries, offset);
- if (k != kh_end(cache->entries)) { /* found it */
- entry = kh_value(cache->entries, k);
- git_atomic_inc(&entry->refcount);
- entry->last_usage = cache->use_ctr++;
- }
- git_mutex_unlock(&cache->lock);
-
- return entry;
-}
-
-/* Run with the cache lock held */
-static void free_lowest_entry(git_pack_cache *cache)
-{
- git_pack_cache_entry *entry;
- khiter_t k;
-
- for (k = kh_begin(cache->entries); k != kh_end(cache->entries); k++) {
- if (!kh_exist(cache->entries, k))
- continue;
-
- entry = kh_value(cache->entries, k);
-
- if (entry && entry->refcount.val == 0) {
- cache->memory_used -= entry->raw.len;
- kh_del(off, cache->entries, k);
- free_cache_object(entry);
- }
- }
-}
-
-static int cache_add(
- git_pack_cache_entry **cached_out,
- git_pack_cache *cache,
- git_rawobj *base,
- git_off_t offset)
-{
- git_pack_cache_entry *entry;
- int error, exists = 0;
- khiter_t k;
-
- if (base->len > GIT_PACK_CACHE_SIZE_LIMIT)
- return -1;
-
- entry = new_cache_object(base);
- if (entry) {
- if (git_mutex_lock(&cache->lock) < 0) {
- giterr_set(GITERR_OS, "failed to lock cache");
- git__free(entry);
- return -1;
- }
- /* Add it to the cache if nobody else has */
- exists = kh_get(off, cache->entries, offset) != kh_end(cache->entries);
- if (!exists) {
- while (cache->memory_used + base->len > cache->memory_limit)
- free_lowest_entry(cache);
-
- k = kh_put(off, cache->entries, offset, &error);
- assert(error != 0);
- kh_value(cache->entries, k) = entry;
- cache->memory_used += entry->raw.len;
-
- *cached_out = entry;
- }
- git_mutex_unlock(&cache->lock);
- /* Somebody beat us to adding it into the cache */
- if (exists) {
- git__free(entry);
- return -1;
- }
- }
-
- return 0;
-}
-
-/***********************************************************
- *
- * PACK INDEX METHODS
- *
- ***********************************************************/
-
-static void pack_index_free(struct git_pack_file *p)
-{
- if (p->oids) {
- git__free(p->oids);
- p->oids = NULL;
- }
- if (p->index_map.data) {
- git_futils_mmap_free(&p->index_map);
- p->index_map.data = NULL;
- }
-}
-
-static int pack_index_check(const char *path, struct git_pack_file *p)
-{
- struct git_pack_idx_header *hdr;
- uint32_t version, nr, i, *index;
- void *idx_map;
- size_t idx_size;
- struct stat st;
- int error;
- /* TODO: properly open the file without access time using O_NOATIME */
- git_file fd = git_futils_open_ro(path);
- if (fd < 0)
- return fd;
-
- if (p_fstat(fd, &st) < 0) {
- p_close(fd);
- giterr_set(GITERR_OS, "Unable to stat pack index '%s'", path);
- return -1;
- }
-
- if (!S_ISREG(st.st_mode) ||
- !git__is_sizet(st.st_size) ||
- (idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20)
- {
- p_close(fd);
- giterr_set(GITERR_ODB, "Invalid pack index '%s'", path);
- return -1;
- }
-
- error = git_futils_mmap_ro(&p->index_map, fd, 0, idx_size);
-
- p_close(fd);
-
- if (error < 0)
- return error;
-
- hdr = idx_map = p->index_map.data;
-
- if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
- version = ntohl(hdr->idx_version);
-
- if (version < 2 || version > 2) {
- git_futils_mmap_free(&p->index_map);
- return packfile_error("unsupported index version");
- }
-
- } else
- version = 1;
-
- nr = 0;
- index = idx_map;
-
- if (version > 1)
- index += 2; /* skip index header */
-
- for (i = 0; i < 256; i++) {
- uint32_t n = ntohl(index[i]);
- if (n < nr) {
- git_futils_mmap_free(&p->index_map);
- return packfile_error("index is non-monotonic");
- }
- nr = n;
- }
-
- if (version == 1) {
- /*
- * Total size:
- * - 256 index entries 4 bytes each
- * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
- * - 20-byte SHA1 of the packfile
- * - 20-byte SHA1 file checksum
- */
- if (idx_size != 4*256 + nr * 24 + 20 + 20) {
- git_futils_mmap_free(&p->index_map);
- return packfile_error("index is corrupted");
- }
- } else if (version == 2) {
- /*
- * Minimum size:
- * - 8 bytes of header
- * - 256 index entries 4 bytes each
- * - 20-byte sha1 entry * nr
- * - 4-byte crc entry * nr
- * - 4-byte offset entry * nr
- * - 20-byte SHA1 of the packfile
- * - 20-byte SHA1 file checksum
- * And after the 4-byte offset table might be a
- * variable sized table containing 8-byte entries
- * for offsets larger than 2^31.
- */
- unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
- unsigned long max_size = min_size;
-
- if (nr)
- max_size += (nr - 1)*8;
-
- if (idx_size < min_size || idx_size > max_size) {
- git_futils_mmap_free(&p->index_map);
- return packfile_error("wrong index size");
- }
- }
-
- p->num_objects = nr;
- p->index_version = version;
- return 0;
-}
-
-static int pack_index_open(struct git_pack_file *p)
-{
- int error = 0;
- size_t name_len;
- git_buf idx_name = GIT_BUF_INIT;
-
- if (p->index_version > -1)
- return 0;
-
- name_len = strlen(p->pack_name);
- assert(name_len > strlen(".pack")); /* checked by git_pack_file alloc */
-
- git_buf_grow(&idx_name, name_len);
- git_buf_put(&idx_name, p->pack_name, name_len - strlen(".pack"));
- git_buf_puts(&idx_name, ".idx");
- if (git_buf_oom(&idx_name)) {
- giterr_set_oom();
- return -1;
- }
-
- if ((error = git_mutex_lock(&p->lock)) < 0) {
- git_buf_free(&idx_name);
- return error;
- }
-
- if (p->index_version == -1)
- error = pack_index_check(idx_name.ptr, p);
-
- git_buf_free(&idx_name);
-
- git_mutex_unlock(&p->lock);
-
- return error;
-}
-
-static unsigned char *pack_window_open(
- struct git_pack_file *p,
- git_mwindow **w_cursor,
- git_off_t offset,
- unsigned int *left)
-{
- if (p->mwf.fd == -1 && packfile_open(p) < 0)
- return NULL;
-
- /* Since packfiles end in a hash of their content and it's
- * pointless to ask for an offset into the middle of that
- * hash, and the pack_window_contains function above wouldn't match
- * don't allow an offset too close to the end of the file.
- *
- * Don't allow a negative offset, as that means we've wrapped
- * around.
- */
- if (offset > (p->mwf.size - 20))
- return NULL;
- if (offset < 0)
- return NULL;
-
- return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
- }
-
-/*
- * The per-object header is a pretty dense thing, which is
- * - first byte: low four bits are "size",
- * then three bits of "type",
- * with the high bit being "size continues".
- * - each byte afterwards: low seven bits are size continuation,
- * with the high bit being "size continues"
- */
-size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_otype type)
-{
- unsigned char *hdr_base;
- unsigned char c;
-
- assert(type >= GIT_OBJ_COMMIT && type <= GIT_OBJ_REF_DELTA);
-
- /* TODO: add support for chunked objects; see git.git 6c0d19b1 */
-
- c = (unsigned char)((type << 4) | (size & 15));
- size >>= 4;
- hdr_base = hdr;
-
- while (size) {
- *hdr++ = c | 0x80;
- c = size & 0x7f;
- size >>= 7;
- }
- *hdr++ = c;
-
- return (hdr - hdr_base);
-}
-
-
-static int packfile_unpack_header1(
- unsigned long *usedp,
- size_t *sizep,
- git_otype *type,
- const unsigned char *buf,
- unsigned long len)
-{
- unsigned shift;
- unsigned long size, c;
- unsigned long used = 0;
-
- c = buf[used++];
- *type = (c >> 4) & 7;
- size = c & 15;
- shift = 4;
- while (c & 0x80) {
- if (len <= used) {
- giterr_set(GITERR_ODB, "buffer too small");
- return GIT_EBUFS;
- }
-
- if (bitsizeof(long) <= shift) {
- *usedp = 0;
- giterr_set(GITERR_ODB, "packfile corrupted");
- return -1;
- }
-
- c = buf[used++];
- size += (c & 0x7f) << shift;
- shift += 7;
- }
-
- *sizep = (size_t)size;
- *usedp = used;
- return 0;
-}
-
-int git_packfile_unpack_header(
- size_t *size_p,
- git_otype *type_p,
- git_mwindow_file *mwf,
- git_mwindow **w_curs,
- git_off_t *curpos)
-{
- unsigned char *base;
- unsigned int left;
- unsigned long used;
- int ret;
-
- /* pack_window_open() assures us we have [base, base + 20) available
- * as a range that we can look at at. (Its actually the hash
- * size that is assured.) With our object header encoding
- * the maximum deflated object size is 2^137, which is just
- * insane, so we know won't exceed what we have been given.
- */
-/* base = pack_window_open(p, w_curs, *curpos, &left); */
- base = git_mwindow_open(mwf, w_curs, *curpos, 20, &left);
- if (base == NULL)
- return GIT_EBUFS;
-
- ret = packfile_unpack_header1(&used, size_p, type_p, base, left);
- git_mwindow_close(w_curs);
- if (ret == GIT_EBUFS)
- return ret;
- else if (ret < 0)
- return packfile_error("header length is zero");
-
- *curpos += used;
- return 0;
-}
-
-int git_packfile_resolve_header(
- size_t *size_p,
- git_otype *type_p,
- struct git_pack_file *p,
- git_off_t offset)
-{
- git_mwindow *w_curs = NULL;
- git_off_t curpos = offset;
- size_t size;
- git_otype type;
- git_off_t base_offset;
- int error;
-
- error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
- if (error < 0)
- return error;
-
- if (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) {
- size_t base_size;
- git_packfile_stream stream;
-
- base_offset = get_delta_base(p, &w_curs, &curpos, type, offset);
- git_mwindow_close(&w_curs);
- if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0)
- return error;
- error = git_delta_read_header_fromstream(&base_size, size_p, &stream);
- git_packfile_stream_free(&stream);
- if (error < 0)
- return error;
- } else {
- *size_p = size;
- base_offset = 0;
- }
-
- while (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) {
- curpos = base_offset;
- error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
- if (error < 0)
- return error;
- if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA)
- break;
- base_offset = get_delta_base(p, &w_curs, &curpos, type, base_offset);
- git_mwindow_close(&w_curs);
- }
- *type_p = type;
-
- return error;
-}
-
-#define SMALL_STACK_SIZE 64
-
-/**
- * Generate the chain of dependencies which we need to get to the
- * object at `off`. `chain` is used a stack, popping gives the right
- * order to apply deltas on. If an object is found in the pack's base
- * cache, we stop calculating there.
- */
-static int pack_dependency_chain(git_dependency_chain *chain_out,
- git_pack_cache_entry **cached_out, git_off_t *cached_off,
- struct pack_chain_elem *small_stack, size_t *stack_sz,
- struct git_pack_file *p, git_off_t obj_offset)
-{
- git_dependency_chain chain = GIT_ARRAY_INIT;
- git_mwindow *w_curs = NULL;
- git_off_t curpos = obj_offset, base_offset;
- int error = 0, use_heap = 0;
- size_t size, elem_pos;
- git_otype type;
-
- elem_pos = 0;
- while (true) {
- struct pack_chain_elem *elem;
- git_pack_cache_entry *cached = NULL;
-
- /* if we have a base cached, we can stop here instead */
- if ((cached = cache_get(&p->bases, obj_offset)) != NULL) {
- *cached_out = cached;
- *cached_off = obj_offset;
- break;
- }
-
- /* if we run out of space on the small stack, use the array */
- if (elem_pos == SMALL_STACK_SIZE) {
- git_array_init_to_size(chain, elem_pos);
- GITERR_CHECK_ARRAY(chain);
- memcpy(chain.ptr, small_stack, elem_pos * sizeof(struct pack_chain_elem));
- chain.size = elem_pos;
- use_heap = 1;
- }
-
- curpos = obj_offset;
- if (!use_heap) {
- elem = &small_stack[elem_pos];
- } else {
- elem = git_array_alloc(chain);
- if (!elem) {
- error = -1;
- goto on_error;
- }
- }
-
- elem->base_key = obj_offset;
-
- error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
-
- if (error < 0)
- goto on_error;
-
- elem->offset = curpos;
- elem->size = size;
- elem->type = type;
- elem->base_key = obj_offset;
-
- if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA)
- break;
-
- base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
- git_mwindow_close(&w_curs);
-
- if (base_offset == 0) {
- error = packfile_error("delta offset is zero");
- goto on_error;
- }
- if (base_offset < 0) { /* must actually be an error code */
- error = (int)base_offset;
- goto on_error;
- }
-
- /* we need to pass the pos *after* the delta-base bit */
- elem->offset = curpos;
-
- /* go through the loop again, but with the new object */
- obj_offset = base_offset;
- elem_pos++;
- }
-
-
- *stack_sz = elem_pos + 1;
- *chain_out = chain;
- return error;
-
-on_error:
- git_array_clear(chain);
- return error;
-}
-
-int git_packfile_unpack(
- git_rawobj *obj,
- struct git_pack_file *p,
- git_off_t *obj_offset)
-{
- git_mwindow *w_curs = NULL;
- git_off_t curpos = *obj_offset;
- int error, free_base = 0;
- git_dependency_chain chain = GIT_ARRAY_INIT;
- struct pack_chain_elem *elem = NULL, *stack;
- git_pack_cache_entry *cached = NULL;
- struct pack_chain_elem small_stack[SMALL_STACK_SIZE];
- size_t stack_size = 0, elem_pos, alloclen;
- git_otype base_type;
-
- /*
- * TODO: optionally check the CRC on the packfile
- */
-
- error = pack_dependency_chain(&chain, &cached, obj_offset, small_stack, &stack_size, p, *obj_offset);
- if (error < 0)
- return error;
-
- obj->data = NULL;
- obj->len = 0;
- obj->type = GIT_OBJ_BAD;
-
- /* let's point to the right stack */
- stack = chain.ptr ? chain.ptr : small_stack;
-
- elem_pos = stack_size;
- if (cached) {
- memcpy(obj, &cached->raw, sizeof(git_rawobj));
- base_type = obj->type;
- elem_pos--; /* stack_size includes the base, which isn't actually there */
- } else {
- elem = &stack[--elem_pos];
- base_type = elem->type;
- }
-
- switch (base_type) {
- case GIT_OBJ_COMMIT:
- case GIT_OBJ_TREE:
- case GIT_OBJ_BLOB:
- case GIT_OBJ_TAG:
- if (!cached) {
- curpos = elem->offset;
- error = packfile_unpack_compressed(obj, p, &w_curs, &curpos, elem->size, elem->type);
- git_mwindow_close(&w_curs);
- base_type = elem->type;
- }
- if (error < 0)
- goto cleanup;
- break;
- case GIT_OBJ_OFS_DELTA:
- case GIT_OBJ_REF_DELTA:
- error = packfile_error("dependency chain ends in a delta");
- goto cleanup;
- default:
- error = packfile_error("invalid packfile type in header");
- goto cleanup;
- }
-
- /*
- * Finding the object we want a cached base element is
- * problematic, as we need to make sure we don't accidentally
- * give the caller the cached object, which it would then feel
- * free to free, so we need to copy the data.
- */
- if (cached && stack_size == 1) {
- void *data = obj->data;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, obj->len, 1);
- obj->data = git__malloc(alloclen);
- GITERR_CHECK_ALLOC(obj->data);
-
- memcpy(obj->data, data, obj->len + 1);
- git_atomic_dec(&cached->refcount);
- goto cleanup;
- }
-
- /* we now apply each consecutive delta until we run out */
- while (elem_pos > 0 && !error) {
- git_rawobj base, delta;
-
- /*
- * We can now try to add the base to the cache, as
- * long as it's not already the cached one.
- */
- if (!cached)
- free_base = !!cache_add(&cached, &p->bases, obj, elem->base_key);
-
- elem = &stack[elem_pos - 1];
- curpos = elem->offset;
- error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type);
- git_mwindow_close(&w_curs);
-
- if (error < 0)
- break;
-
- /* the current object becomes the new base, on which we apply the delta */
- base = *obj;
- obj->data = NULL;
- obj->len = 0;
- obj->type = GIT_OBJ_BAD;
-
- error = git_delta_apply(&obj->data, &obj->len, base.data, base.len, delta.data, delta.len);
- obj->type = base_type;
-
- /*
- * We usually don't want to free the base at this
- * point, as we put it into the cache in the previous
- * iteration. free_base lets us know that we got the
- * base object directly from the packfile, so we can free it.
- */
- git__free(delta.data);
- if (free_base) {
- free_base = 0;
- git__free(base.data);
- }
-
- if (cached) {
- git_atomic_dec(&cached->refcount);
- cached = NULL;
- }
-
- if (error < 0)
- break;
-
- elem_pos--;
- }
-
-cleanup:
- if (error < 0) {
- git__free(obj->data);
- if (cached)
- git_atomic_dec(&cached->refcount);
- }
-
- if (elem)
- *obj_offset = curpos;
-
- git_array_clear(chain);
- return error;
-}
-
-static void *use_git_alloc(void *opaq, unsigned int count, unsigned int size)
-{
- GIT_UNUSED(opaq);
- return git__calloc(count, size);
-}
-
-static void use_git_free(void *opaq, void *ptr)
-{
- GIT_UNUSED(opaq);
- git__free(ptr);
-}
-
-int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, git_off_t curpos)
-{
- int st;
-
- memset(obj, 0, sizeof(git_packfile_stream));
- obj->curpos = curpos;
- obj->p = p;
- obj->zstream.zalloc = use_git_alloc;
- obj->zstream.zfree = use_git_free;
- obj->zstream.next_in = Z_NULL;
- obj->zstream.next_out = Z_NULL;
- st = inflateInit(&obj->zstream);
- if (st != Z_OK) {
- giterr_set(GITERR_ZLIB, "failed to init packfile stream");
- return -1;
- }
-
- return 0;
-}
-
-ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len)
-{
- unsigned char *in;
- size_t written;
- int st;
-
- if (obj->done)
- return 0;
-
- in = pack_window_open(obj->p, &obj->mw, obj->curpos, &obj->zstream.avail_in);
- if (in == NULL)
- return GIT_EBUFS;
-
- obj->zstream.next_out = buffer;
- obj->zstream.avail_out = (unsigned int)len;
- obj->zstream.next_in = in;
-
- st = inflate(&obj->zstream, Z_SYNC_FLUSH);
- git_mwindow_close(&obj->mw);
-
- obj->curpos += obj->zstream.next_in - in;
- written = len - obj->zstream.avail_out;
-
- if (st != Z_OK && st != Z_STREAM_END) {
- giterr_set(GITERR_ZLIB, "error reading from the zlib stream");
- return -1;
- }
-
- if (st == Z_STREAM_END)
- obj->done = 1;
-
-
- /* If we didn't write anything out but we're not done, we need more data */
- if (!written && st != Z_STREAM_END)
- return GIT_EBUFS;
-
- return written;
-
-}
-
-void git_packfile_stream_free(git_packfile_stream *obj)
-{
- inflateEnd(&obj->zstream);
-}
-
-static int packfile_unpack_compressed(
- git_rawobj *obj,
- struct git_pack_file *p,
- git_mwindow **w_curs,
- git_off_t *curpos,
- size_t size,
- git_otype type)
-{
- size_t buf_size;
- int st;
- z_stream stream;
- unsigned char *buffer, *in;
-
- GITERR_CHECK_ALLOC_ADD(&buf_size, size, 1);
- buffer = git__calloc(1, buf_size);
- GITERR_CHECK_ALLOC(buffer);
-
- memset(&stream, 0, sizeof(stream));
- stream.next_out = buffer;
- stream.avail_out = (uInt)buf_size;
- stream.zalloc = use_git_alloc;
- stream.zfree = use_git_free;
-
- st = inflateInit(&stream);
- if (st != Z_OK) {
- git__free(buffer);
- giterr_set(GITERR_ZLIB, "failed to init zlib stream on unpack");
-
- return -1;
- }
-
- do {
- in = pack_window_open(p, w_curs, *curpos, &stream.avail_in);
- stream.next_in = in;
- st = inflate(&stream, Z_FINISH);
- git_mwindow_close(w_curs);
-
- if (!stream.avail_out)
- break; /* the payload is larger than it should be */
-
- if (st == Z_BUF_ERROR && in == NULL) {
- inflateEnd(&stream);
- git__free(buffer);
- return GIT_EBUFS;
- }
-
- *curpos += stream.next_in - in;
- } while (st == Z_OK || st == Z_BUF_ERROR);
-
- inflateEnd(&stream);
-
- if ((st != Z_STREAM_END) || stream.total_out != size) {
- git__free(buffer);
- giterr_set(GITERR_ZLIB, "error inflating zlib stream");
- return -1;
- }
-
- obj->type = type;
- obj->len = size;
- obj->data = buffer;
- return 0;
-}
-
-/*
- * curpos is where the data starts, delta_obj_offset is the where the
- * header starts
- */
-git_off_t get_delta_base(
- struct git_pack_file *p,
- git_mwindow **w_curs,
- git_off_t *curpos,
- git_otype type,
- git_off_t delta_obj_offset)
-{
- unsigned int left = 0;
- unsigned char *base_info;
- git_off_t base_offset;
- git_oid unused;
-
- base_info = pack_window_open(p, w_curs, *curpos, &left);
- /* Assumption: the only reason this would fail is because the file is too small */
- if (base_info == NULL)
- return GIT_EBUFS;
- /* pack_window_open() assured us we have [base_info, base_info + 20)
- * as a range that we can look at without walking off the
- * end of the mapped window. Its actually the hash size
- * that is assured. An OFS_DELTA longer than the hash size
- * is stupid, as then a REF_DELTA would be smaller to store.
- */
- if (type == GIT_OBJ_OFS_DELTA) {
- unsigned used = 0;
- unsigned char c = base_info[used++];
- base_offset = c & 127;
- while (c & 128) {
- if (left <= used)
- return GIT_EBUFS;
- base_offset += 1;
- if (!base_offset || MSB(base_offset, 7))
- return 0; /* overflow */
- c = base_info[used++];
- base_offset = (base_offset << 7) + (c & 127);
- }
- base_offset = delta_obj_offset - base_offset;
- if (base_offset <= 0 || base_offset >= delta_obj_offset)
- return 0; /* out of bound */
- *curpos += used;
- } else if (type == GIT_OBJ_REF_DELTA) {
- /* If we have the cooperative cache, search in it first */
- if (p->has_cache) {
- khiter_t k;
- git_oid oid;
-
- git_oid_fromraw(&oid, base_info);
- k = kh_get(oid, p->idx_cache, &oid);
- if (k != kh_end(p->idx_cache)) {
- *curpos += 20;
- return ((struct git_pack_entry *)kh_value(p->idx_cache, k))->offset;
- } else {
- /* If we're building an index, don't try to find the pack
- * entry; we just haven't seen it yet. We'll make
- * progress again in the next loop.
- */
- return GIT_PASSTHROUGH;
- }
- }
-
- /* The base entry _must_ be in the same pack */
- if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < 0)
- return packfile_error("base entry delta is not in the same pack");
- *curpos += 20;
- } else
- return 0;
-
- return base_offset;
-}
-
-/***********************************************************
- *
- * PACKFILE METHODS
- *
- ***********************************************************/
-
-void git_packfile_free(struct git_pack_file *p)
-{
- if (!p)
- return;
-
- cache_free(&p->bases);
-
- if (p->mwf.fd >= 0) {
- git_mwindow_free_all_locked(&p->mwf);
- p_close(p->mwf.fd);
- }
-
- pack_index_free(p);
-
- git__free(p->bad_object_sha1);
-
- git_mutex_free(&p->lock);
- git_mutex_free(&p->bases.lock);
- git__free(p);
-}
-
-static int packfile_open(struct git_pack_file *p)
-{
- struct stat st;
- struct git_pack_header hdr;
- git_oid sha1;
- unsigned char *idx_sha1;
-
- if (p->index_version == -1 && pack_index_open(p) < 0)
- return git_odb__error_notfound("failed to open packfile", NULL, 0);
-
- /* if mwf opened by another thread, return now */
- if (git_mutex_lock(&p->lock) < 0)
- return packfile_error("failed to get lock for open");
-
- if (p->mwf.fd >= 0) {
- git_mutex_unlock(&p->lock);
- return 0;
- }
-
- /* TODO: open with noatime */
- p->mwf.fd = git_futils_open_ro(p->pack_name);
- if (p->mwf.fd < 0)
- goto cleanup;
-
- if (p_fstat(p->mwf.fd, &st) < 0 ||
- git_mwindow_file_register(&p->mwf) < 0)
- goto cleanup;
-
- /* If we created the struct before we had the pack we lack size. */
- if (!p->mwf.size) {
- if (!S_ISREG(st.st_mode))
- goto cleanup;
- p->mwf.size = (git_off_t)st.st_size;
- } else if (p->mwf.size != st.st_size)
- goto cleanup;
-
-#if 0
- /* We leave these file descriptors open with sliding mmap;
- * there is no point keeping them open across exec(), though.
- */
- fd_flag = fcntl(p->mwf.fd, F_GETFD, 0);
- if (fd_flag < 0)
- goto cleanup;
-
- fd_flag |= FD_CLOEXEC;
- if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)
- goto cleanup;
-#endif
-
- /* Verify we recognize this pack file format. */
- if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < 0 ||
- hdr.hdr_signature != htonl(PACK_SIGNATURE) ||
- !pack_version_ok(hdr.hdr_version))
- goto cleanup;
-
- /* Verify the pack matches its index. */
- if (p->num_objects != ntohl(hdr.hdr_entries) ||
- p_lseek(p->mwf.fd, p->mwf.size - GIT_OID_RAWSZ, SEEK_SET) == -1 ||
- p_read(p->mwf.fd, sha1.id, GIT_OID_RAWSZ) < 0)
- goto cleanup;
-
- idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;
-
- if (git_oid__cmp(&sha1, (git_oid *)idx_sha1) != 0)
- goto cleanup;
-
- git_mutex_unlock(&p->lock);
- return 0;
-
-cleanup:
- giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name);
-
- if (p->mwf.fd >= 0)
- p_close(p->mwf.fd);
- p->mwf.fd = -1;
-
- git_mutex_unlock(&p->lock);
-
- return -1;
-}
-
-int git_packfile__name(char **out, const char *path)
-{
- size_t path_len;
- git_buf buf = GIT_BUF_INIT;
-
- path_len = strlen(path);
-
- if (path_len < strlen(".idx"))
- return git_odb__error_notfound("invalid packfile path", NULL, 0);
-
- if (git_buf_printf(&buf, "%.*s.pack", (int)(path_len - strlen(".idx")), path) < 0)
- return -1;
-
- *out = git_buf_detach(&buf);
- return 0;
-}
-
-int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
-{
- struct stat st;
- struct git_pack_file *p;
- size_t path_len = path ? strlen(path) : 0, alloc_len;
-
- *pack_out = NULL;
-
- if (path_len < strlen(".idx"))
- return git_odb__error_notfound("invalid packfile path", NULL, 0);
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*p), path_len);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
-
- p = git__calloc(1, alloc_len);
- GITERR_CHECK_ALLOC(p);
-
- memcpy(p->pack_name, path, path_len + 1);
-
- /*
- * Make sure a corresponding .pack file exists and that
- * the index looks sane.
- */
- if (git__suffixcmp(path, ".idx") == 0) {
- size_t root_len = path_len - strlen(".idx");
-
- memcpy(p->pack_name + root_len, ".keep", sizeof(".keep"));
- if (git_path_exists(p->pack_name) == true)
- p->pack_keep = 1;
-
- memcpy(p->pack_name + root_len, ".pack", sizeof(".pack"));
- }
-
- if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
- git__free(p);
- return git_odb__error_notfound("packfile not found", NULL, 0);
- }
-
- /* ok, it looks sane as far as we can check without
- * actually mapping the pack file.
- */
- p->mwf.fd = -1;
- p->mwf.size = st.st_size;
- p->pack_local = 1;
- p->mtime = (git_time_t)st.st_mtime;
- p->index_version = -1;
-
- if (git_mutex_init(&p->lock)) {
- giterr_set(GITERR_OS, "Failed to initialize packfile mutex");
- git__free(p);
- return -1;
- }
-
- if (cache_init(&p->bases) < 0) {
- git__free(p);
- return -1;
- }
-
- *pack_out = p;
-
- return 0;
-}
-
-/***********************************************************
- *
- * PACKFILE ENTRY SEARCH INTERNALS
- *
- ***********************************************************/
-
-static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n)
-{
- const unsigned char *index = p->index_map.data;
- const unsigned char *end = index + p->index_map.len;
- index += 4 * 256;
- if (p->index_version == 1) {
- return ntohl(*((uint32_t *)(index + 24 * n)));
- } else {
- uint32_t off;
- index += 8 + p->num_objects * (20 + 4);
- off = ntohl(*((uint32_t *)(index + 4 * n)));
- if (!(off & 0x80000000))
- return off;
- index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
-
- /* Make sure we're not being sent out of bounds */
- if (index >= end - 8)
- return -1;
-
- return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
- ntohl(*((uint32_t *)(index + 4)));
- }
-}
-
-static int git__memcmp4(const void *a, const void *b) {
- return memcmp(a, b, 4);
-}
-
-int git_pack_foreach_entry(
- struct git_pack_file *p,
- git_odb_foreach_cb cb,
- void *data)
-{
- const unsigned char *index = p->index_map.data, *current;
- uint32_t i;
- int error = 0;
-
- if (index == NULL) {
- if ((error = pack_index_open(p)) < 0)
- return error;
-
- assert(p->index_map.data);
-
- index = p->index_map.data;
- }
-
- if (p->index_version > 1) {
- index += 8;
- }
-
- index += 4 * 256;
-
- if (p->oids == NULL) {
- git_vector offsets, oids;
-
- if ((error = git_vector_init(&oids, p->num_objects, NULL)))
- return error;
-
- if ((error = git_vector_init(&offsets, p->num_objects, git__memcmp4)))
- return error;
-
- if (p->index_version > 1) {
- const unsigned char *off = index + 24 * p->num_objects;
- for (i = 0; i < p->num_objects; i++)
- git_vector_insert(&offsets, (void*)&off[4 * i]);
- git_vector_sort(&offsets);
- git_vector_foreach(&offsets, i, current)
- git_vector_insert(&oids, (void*)&index[5 * (current - off)]);
- } else {
- for (i = 0; i < p->num_objects; i++)
- git_vector_insert(&offsets, (void*)&index[24 * i]);
- git_vector_sort(&offsets);
- git_vector_foreach(&offsets, i, current)
- git_vector_insert(&oids, (void*)¤t[4]);
- }
-
- git_vector_free(&offsets);
- p->oids = (git_oid **)git_vector_detach(NULL, NULL, &oids);
- }
-
- for (i = 0; i < p->num_objects; i++)
- if ((error = cb(p->oids[i], data)) != 0)
- return giterr_set_after_callback(error);
-
- return error;
-}
-
-static int pack_entry_find_offset(
- git_off_t *offset_out,
- git_oid *found_oid,
- struct git_pack_file *p,
- const git_oid *short_oid,
- size_t len)
-{
- const uint32_t *level1_ofs;
- const unsigned char *index;
- unsigned hi, lo, stride;
- int pos, found = 0;
- git_off_t offset;
- const unsigned char *current = 0;
-
- *offset_out = 0;
-
- if (p->index_version == -1) {
- int error;
-
- if ((error = pack_index_open(p)) < 0)
- return error;
- assert(p->index_map.data);
- }
-
- index = p->index_map.data;
- level1_ofs = p->index_map.data;
-
- if (p->index_version > 1) {
- level1_ofs += 2;
- index += 8;
- }
-
- index += 4 * 256;
- hi = ntohl(level1_ofs[(int)short_oid->id[0]]);
- lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)short_oid->id[0] - 1]));
-
- if (p->index_version > 1) {
- stride = 20;
- } else {
- stride = 24;
- index += 4;
- }
-
-#ifdef INDEX_DEBUG_LOOKUP
- printf("%02x%02x%02x... lo %u hi %u nr %d\n",
- short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
-#endif
-
-#ifdef GIT_USE_LOOKUP
- pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id);
-#else
- pos = sha1_position(index, stride, lo, hi, short_oid->id);
-#endif
-
- if (pos >= 0) {
- /* An object matching exactly the oid was found */
- found = 1;
- current = index + pos * stride;
- } else {
- /* No object was found */
- /* pos refers to the object with the "closest" oid to short_oid */
- pos = - 1 - pos;
- if (pos < (int)p->num_objects) {
- current = index + pos * stride;
-
- if (!git_oid_ncmp(short_oid, (const git_oid *)current, len))
- found = 1;
- }
- }
-
- if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)p->num_objects) {
- /* Check for ambiguousity */
- const unsigned char *next = current + stride;
-
- if (!git_oid_ncmp(short_oid, (const git_oid *)next, len)) {
- found = 2;
- }
- }
-
- if (!found)
- return git_odb__error_notfound("failed to find offset for pack entry", short_oid, len);
- if (found > 1)
- return git_odb__error_ambiguous("found multiple offsets for pack entry");
-
- if ((offset = nth_packed_object_offset(p, pos)) < 0) {
- giterr_set(GITERR_ODB, "packfile index is corrupt");
- return -1;
- }
-
- *offset_out = offset;
- git_oid_fromraw(found_oid, current);
-
-#ifdef INDEX_DEBUG_LOOKUP
- {
- unsigned char hex_sha1[GIT_OID_HEXSZ + 1];
- git_oid_fmt(hex_sha1, found_oid);
- hex_sha1[GIT_OID_HEXSZ] = '\0';
- printf("found lo=%d %s\n", lo, hex_sha1);
- }
-#endif
-
- return 0;
-}
-
-int git_pack_entry_find(
- struct git_pack_entry *e,
- struct git_pack_file *p,
- const git_oid *short_oid,
- size_t len)
-{
- git_off_t offset;
- git_oid found_oid;
- int error;
-
- assert(p);
-
- if (len == GIT_OID_HEXSZ && p->num_bad_objects) {
- unsigned i;
- for (i = 0; i < p->num_bad_objects; i++)
- if (git_oid__cmp(short_oid, &p->bad_object_sha1[i]) == 0)
- return packfile_error("bad object found in packfile");
- }
-
- error = pack_entry_find_offset(&offset, &found_oid, p, short_oid, len);
- if (error < 0)
- return error;
-
- /* we found a unique entry in the index;
- * make sure the packfile backing the index
- * still exists on disk */
- if (p->mwf.fd == -1 && (error = packfile_open(p)) < 0)
- return error;
-
- e->offset = offset;
- e->p = p;
-
- git_oid_cpy(&e->sha1, &found_oid);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_pack_h__
-#define INCLUDE_pack_h__
-
-#include <zlib.h>
-
-#include "git2/oid.h"
-
-#include "common.h"
-#include "map.h"
-#include "mwindow.h"
-#include "odb.h"
-#include "oidmap.h"
-#include "array.h"
-
-#define GIT_PACK_FILE_MODE 0444
-
-#define PACK_SIGNATURE 0x5041434b /* "PACK" */
-#define PACK_VERSION 2
-#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
-struct git_pack_header {
- uint32_t hdr_signature;
- uint32_t hdr_version;
- uint32_t hdr_entries;
-};
-
-/*
- * The first four bytes of index formats later than version 1 should
- * start with this signature, as all older git binaries would find this
- * value illegal and abort reading the file.
- *
- * This is the case because the number of objects in a packfile
- * cannot exceed 1,431,660,000 as every object would need at least
- * 3 bytes of data and the overall packfile cannot exceed 4 GiB with
- * version 1 of the index file due to the offsets limited to 32 bits.
- * Clearly the signature exceeds this maximum.
- *
- * Very old git binaries will also compare the first 4 bytes to the
- * next 4 bytes in the index and abort with a "non-monotonic index"
- * error if the second 4 byte word is smaller than the first 4
- * byte word. This would be true in the proposed future index
- * format as idx_signature would be greater than idx_version.
- */
-
-#define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */
-
-struct git_pack_idx_header {
- uint32_t idx_signature;
- uint32_t idx_version;
-};
-
-typedef struct git_pack_cache_entry {
- size_t last_usage; /* enough? */
- git_atomic refcount;
- git_rawobj raw;
-} git_pack_cache_entry;
-
-struct pack_chain_elem {
- git_off_t base_key;
- git_off_t offset;
- size_t size;
- git_otype type;
-};
-
-typedef git_array_t(struct pack_chain_elem) git_dependency_chain;
-
-#include "offmap.h"
-#include "oidmap.h"
-
-#define GIT_PACK_CACHE_MEMORY_LIMIT 16 * 1024 * 1024
-#define GIT_PACK_CACHE_SIZE_LIMIT 1024 * 1024 /* don't bother caching anything over 1MB */
-
-typedef struct {
- size_t memory_used;
- size_t memory_limit;
- size_t use_ctr;
- git_mutex lock;
- git_offmap *entries;
-} git_pack_cache;
-
-struct git_pack_file {
- git_mwindow_file mwf;
- git_map index_map;
- git_mutex lock; /* protect updates to mwf and index_map */
- git_atomic refcount;
-
- uint32_t num_objects;
- uint32_t num_bad_objects;
- git_oid *bad_object_sha1; /* array of git_oid */
-
- int index_version;
- git_time_t mtime;
- unsigned pack_local:1, pack_keep:1, has_cache:1;
- git_oidmap *idx_cache;
- git_oid **oids;
-
- git_pack_cache bases; /* delta base cache */
-
- time_t last_freshen; /* last time the packfile was freshened */
-
- /* something like ".git/objects/pack/xxxxx.pack" */
- char pack_name[GIT_FLEX_ARRAY]; /* more */
-};
-
-struct git_pack_entry {
- git_off_t offset;
- git_oid sha1;
- struct git_pack_file *p;
-};
-
-typedef struct git_packfile_stream {
- git_off_t curpos;
- int done;
- z_stream zstream;
- struct git_pack_file *p;
- git_mwindow *mw;
-} git_packfile_stream;
-
-size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_otype type);
-
-int git_packfile__name(char **out, const char *path);
-
-int git_packfile_unpack_header(
- size_t *size_p,
- git_otype *type_p,
- git_mwindow_file *mwf,
- git_mwindow **w_curs,
- git_off_t *curpos);
-
-int git_packfile_resolve_header(
- size_t *size_p,
- git_otype *type_p,
- struct git_pack_file *p,
- git_off_t offset);
-
-int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset);
-
-int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, git_off_t curpos);
-ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len);
-void git_packfile_stream_free(git_packfile_stream *obj);
-
-git_off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs,
- git_off_t *curpos, git_otype type,
- git_off_t delta_obj_offset);
-
-void git_packfile_free(struct git_pack_file *p);
-int git_packfile_alloc(struct git_pack_file **pack_out, const char *path);
-
-int git_pack_entry_find(
- struct git_pack_entry *e,
- struct git_pack_file *p,
- const git_oid *short_oid,
- size_t len);
-int git_pack_foreach_entry(
- struct git_pack_file *p,
- git_odb_foreach_cb cb,
- void *data);
-
-#endif
+++ /dev/null
-#include "git2/patch.h"
-#include "diff.h"
-#include "patch.h"
-
-
-int git_patch__invoke_callbacks(
- git_patch *patch,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb line_cb,
- void *payload)
-{
- int error = 0;
- uint32_t i, j;
-
- if (file_cb)
- error = file_cb(patch->delta, 0, payload);
-
- if (error)
- return error;
-
- if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
- if (binary_cb)
- error = binary_cb(patch->delta, &patch->binary, payload);
-
- return error;
- }
-
- if (!hunk_cb && !line_cb)
- return error;
-
- for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
- git_patch_hunk *h = git_array_get(patch->hunks, i);
-
- if (hunk_cb)
- error = hunk_cb(patch->delta, &h->hunk, payload);
-
- if (!line_cb)
- continue;
-
- for (j = 0; !error && j < h->line_count; ++j) {
- git_diff_line *l =
- git_array_get(patch->lines, h->line_start + j);
-
- error = line_cb(patch->delta, &h->hunk, l, payload);
- }
- }
-
- return error;
-}
-
-size_t git_patch_size(
- git_patch *patch,
- int include_context,
- int include_hunk_headers,
- int include_file_headers)
-{
- size_t out;
-
- assert(patch);
-
- out = patch->content_size;
-
- if (!include_context)
- out -= patch->context_size;
-
- if (include_hunk_headers)
- out += patch->header_size;
-
- if (include_file_headers) {
- git_buf file_header = GIT_BUF_INIT;
-
- if (git_diff_delta__format_file_header(
- &file_header, patch->delta, NULL, NULL, 0) < 0)
- giterr_clear();
- else
- out += git_buf_len(&file_header);
-
- git_buf_free(&file_header);
- }
-
- return out;
-}
-
-int git_patch_line_stats(
- size_t *total_ctxt,
- size_t *total_adds,
- size_t *total_dels,
- const git_patch *patch)
-{
- size_t totals[3], idx;
-
- memset(totals, 0, sizeof(totals));
-
- for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
- git_diff_line *line = git_array_get(patch->lines, idx);
- if (!line)
- continue;
-
- switch (line->origin) {
- case GIT_DIFF_LINE_CONTEXT: totals[0]++; break;
- case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
- case GIT_DIFF_LINE_DELETION: totals[2]++; break;
- default:
- /* diff --stat and --numstat don't count EOFNL marks because
- * they will always be paired with a ADDITION or DELETION line.
- */
- break;
- }
- }
-
- if (total_ctxt)
- *total_ctxt = totals[0];
- if (total_adds)
- *total_adds = totals[1];
- if (total_dels)
- *total_dels = totals[2];
-
- return 0;
-}
-
-const git_diff_delta *git_patch_get_delta(const git_patch *patch)
-{
- assert(patch);
- return patch->delta;
-}
-
-size_t git_patch_num_hunks(const git_patch *patch)
-{
- assert(patch);
- return git_array_size(patch->hunks);
-}
-
-static int patch_error_outofrange(const char *thing)
-{
- giterr_set(GITERR_INVALID, "patch %s index out of range", thing);
- return GIT_ENOTFOUND;
-}
-
-int git_patch_get_hunk(
- const git_diff_hunk **out,
- size_t *lines_in_hunk,
- git_patch *patch,
- size_t hunk_idx)
-{
- git_patch_hunk *hunk;
- assert(patch);
-
- hunk = git_array_get(patch->hunks, hunk_idx);
-
- if (!hunk) {
- if (out) *out = NULL;
- if (lines_in_hunk) *lines_in_hunk = 0;
- return patch_error_outofrange("hunk");
- }
-
- if (out) *out = &hunk->hunk;
- if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
- return 0;
-}
-
-int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx)
-{
- git_patch_hunk *hunk;
- assert(patch);
-
- if (!(hunk = git_array_get(patch->hunks, hunk_idx)))
- return patch_error_outofrange("hunk");
- return (int)hunk->line_count;
-}
-
-int git_patch_get_line_in_hunk(
- const git_diff_line **out,
- git_patch *patch,
- size_t hunk_idx,
- size_t line_of_hunk)
-{
- git_patch_hunk *hunk;
- git_diff_line *line;
-
- assert(patch);
-
- if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
- if (out) *out = NULL;
- return patch_error_outofrange("hunk");
- }
-
- if (line_of_hunk >= hunk->line_count ||
- !(line = git_array_get(
- patch->lines, hunk->line_start + line_of_hunk))) {
- if (out) *out = NULL;
- return patch_error_outofrange("line");
- }
-
- if (out) *out = line;
- return 0;
-}
-
-int git_patch_from_diff(git_patch **out, git_diff *diff, size_t idx)
-{
- assert(out && diff && diff->patch_fn);
- return diff->patch_fn(out, diff, idx);
-}
-
-static void git_patch__free(git_patch *patch)
-{
- if (patch->free_fn)
- patch->free_fn(patch);
-}
-
-void git_patch_free(git_patch *patch)
-{
- if (patch)
- GIT_REFCOUNT_DEC(patch, git_patch__free);
-}
+++ /dev/null
-/*
-* Copyright (C) the libgit2 contributors. All rights reserved.
-*
-* This file is part of libgit2, distributed under the GNU GPL v2 with
-* a Linking Exception. For full terms see the included COPYING file.
-*/
-#ifndef INCLUDE_patch_h__
-#define INCLUDE_patch_h__
-
-#include "git2/patch.h"
-#include "array.h"
-
-/* cached information about a hunk in a patch */
-typedef struct git_patch_hunk {
- git_diff_hunk hunk;
- size_t line_start;
- size_t line_count;
-} git_patch_hunk;
-
-struct git_patch {
- git_refcount rc;
-
- git_repository *repo; /* may be null */
-
- git_diff_options diff_opts;
-
- git_diff_delta *delta;
- git_diff_binary binary;
- git_array_t(git_patch_hunk) hunks;
- git_array_t(git_diff_line) lines;
-
- size_t header_size;
- size_t content_size;
- size_t context_size;
-
- void (*free_fn)(git_patch *patch);
-};
-
-extern int git_patch__invoke_callbacks(
- git_patch *patch,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb line_cb,
- void *payload);
-
-extern int git_patch_line_stats(
- size_t *total_ctxt,
- size_t *total_adds,
- size_t *total_dels,
- const git_patch *patch);
-
-/** Options for parsing patch files. */
-typedef struct {
- /**
- * The length of the prefix (in path segments) for the filenames.
- * This prefix will be removed when looking for files. The default is 1.
- */
- uint32_t prefix_len;
-} git_patch_options;
-
-#define GIT_PATCH_OPTIONS_INIT { 1 }
-
-extern void git_patch_free(git_patch *patch);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "git2/blob.h"
-#include "diff.h"
-#include "diff_generate.h"
-#include "diff_file.h"
-#include "diff_driver.h"
-#include "patch_generate.h"
-#include "diff_xdiff.h"
-#include "delta.h"
-#include "zstream.h"
-#include "fileops.h"
-
-static void diff_output_init(
- git_patch_generated_output *, const git_diff_options *, git_diff_file_cb,
- git_diff_binary_cb, git_diff_hunk_cb, git_diff_line_cb, void*);
-
-static void diff_output_to_patch(
- git_patch_generated_output *, git_patch_generated *);
-
-static void patch_generated_free(git_patch *p)
-{
- git_patch_generated *patch = (git_patch_generated *)p;
-
- git_array_clear(patch->base.lines);
- git_array_clear(patch->base.hunks);
-
- git__free((char *)patch->base.binary.old_file.data);
- git__free((char *)patch->base.binary.new_file.data);
-
- git_diff_file_content__clear(&patch->ofile);
- git_diff_file_content__clear(&patch->nfile);
-
- git_diff_free(patch->diff); /* decrements refcount */
- patch->diff = NULL;
-
- git_pool_clear(&patch->flattened);
-
- git__free((char *)patch->base.diff_opts.old_prefix);
- git__free((char *)patch->base.diff_opts.new_prefix);
-
- if (patch->flags & GIT_PATCH_GENERATED_ALLOCATED)
- git__free(patch);
-}
-
-static void patch_generated_update_binary(git_patch_generated *patch)
-{
- if ((patch->base.delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
- return;
-
- if ((patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0 ||
- (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
- patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
-
- else if (patch->ofile.file->size > GIT_XDIFF_MAX_SIZE ||
- patch->nfile.file->size > GIT_XDIFF_MAX_SIZE)
- patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
-
- else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 &&
- (patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0)
- patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
-}
-
-static void patch_generated_init_common(git_patch_generated *patch)
-{
- patch->base.free_fn = patch_generated_free;
-
- patch_generated_update_binary(patch);
-
- patch->flags |= GIT_PATCH_GENERATED_INITIALIZED;
-
- if (patch->diff)
- git_diff_addref(patch->diff);
-}
-
-static int patch_generated_normalize_options(
- git_diff_options *out,
- const git_diff_options *opts)
-{
- if (opts) {
- GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
- memcpy(out, opts, sizeof(git_diff_options));
- } else {
- git_diff_options default_opts = GIT_DIFF_OPTIONS_INIT;
- memcpy(out, &default_opts, sizeof(git_diff_options));
- }
-
- out->old_prefix = opts && opts->old_prefix ?
- git__strdup(opts->old_prefix) :
- git__strdup(DIFF_OLD_PREFIX_DEFAULT);
-
- out->new_prefix = opts && opts->new_prefix ?
- git__strdup(opts->new_prefix) :
- git__strdup(DIFF_NEW_PREFIX_DEFAULT);
-
- GITERR_CHECK_ALLOC(out->old_prefix);
- GITERR_CHECK_ALLOC(out->new_prefix);
-
- return 0;
-}
-
-static int patch_generated_init(
- git_patch_generated *patch, git_diff *diff, size_t delta_index)
-{
- int error = 0;
-
- memset(patch, 0, sizeof(*patch));
-
- patch->diff = diff;
- patch->base.repo = diff->repo;
- patch->base.delta = git_vector_get(&diff->deltas, delta_index);
- patch->delta_index = delta_index;
-
- if ((error = patch_generated_normalize_options(
- &patch->base.diff_opts, &diff->opts)) < 0 ||
- (error = git_diff_file_content__init_from_diff(
- &patch->ofile, diff, patch->base.delta, true)) < 0 ||
- (error = git_diff_file_content__init_from_diff(
- &patch->nfile, diff, patch->base.delta, false)) < 0)
- return error;
-
- patch_generated_init_common(patch);
-
- return 0;
-}
-
-static int patch_generated_alloc_from_diff(
- git_patch_generated **out, git_diff *diff, size_t delta_index)
-{
- int error;
- git_patch_generated *patch = git__calloc(1, sizeof(git_patch_generated));
- GITERR_CHECK_ALLOC(patch);
-
- if (!(error = patch_generated_init(patch, diff, delta_index))) {
- patch->flags |= GIT_PATCH_GENERATED_ALLOCATED;
- GIT_REFCOUNT_INC(patch);
- } else {
- git__free(patch);
- patch = NULL;
- }
-
- *out = patch;
- return error;
-}
-
-GIT_INLINE(bool) should_skip_binary(git_patch_generated *patch, git_diff_file *file)
-{
- if ((patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) != 0)
- return false;
-
- return (file->flags & GIT_DIFF_FLAG_BINARY) != 0;
-}
-
-static bool patch_generated_diffable(git_patch_generated *patch)
-{
- size_t olen, nlen;
-
- if (patch->base.delta->status == GIT_DELTA_UNMODIFIED)
- return false;
-
- /* if we've determined this to be binary (and we are not showing binary
- * data) then we have skipped loading the map data. instead, query the
- * file data itself.
- */
- if ((patch->base.delta->flags & GIT_DIFF_FLAG_BINARY) != 0 &&
- (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) == 0) {
- olen = (size_t)patch->ofile.file->size;
- nlen = (size_t)patch->nfile.file->size;
- } else {
- olen = patch->ofile.map.len;
- nlen = patch->nfile.map.len;
- }
-
- /* if both sides are empty, files are identical */
- if (!olen && !nlen)
- return false;
-
- /* otherwise, check the file sizes and the oid */
- return (olen != nlen ||
- !git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id));
-}
-
-static int patch_generated_load(git_patch_generated *patch, git_patch_generated_output *output)
-{
- int error = 0;
- bool incomplete_data;
-
- if ((patch->flags & GIT_PATCH_GENERATED_LOADED) != 0)
- return 0;
-
- /* if no hunk and data callbacks and user doesn't care if data looks
- * binary, then there is no need to actually load the data
- */
- if ((patch->ofile.opts_flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0 &&
- output && !output->binary_cb && !output->hunk_cb && !output->data_cb)
- return 0;
-
- incomplete_data =
- (((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
- (patch->ofile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0) &&
- ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
- (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0));
-
- /* always try to load workdir content first because filtering may
- * need 2x data size and this minimizes peak memory footprint
- */
- if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) {
- if ((error = git_diff_file_content__load(
- &patch->ofile, &patch->base.diff_opts)) < 0 ||
- should_skip_binary(patch, patch->ofile.file))
- goto cleanup;
- }
- if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) {
- if ((error = git_diff_file_content__load(
- &patch->nfile, &patch->base.diff_opts)) < 0 ||
- should_skip_binary(patch, patch->nfile.file))
- goto cleanup;
- }
-
- /* once workdir has been tried, load other data as needed */
- if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) {
- if ((error = git_diff_file_content__load(
- &patch->ofile, &patch->base.diff_opts)) < 0 ||
- should_skip_binary(patch, patch->ofile.file))
- goto cleanup;
- }
- if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) {
- if ((error = git_diff_file_content__load(
- &patch->nfile, &patch->base.diff_opts)) < 0 ||
- should_skip_binary(patch, patch->nfile.file))
- goto cleanup;
- }
-
- /* if previously missing an oid, and now that we have it the two sides
- * are the same (and not submodules), update MODIFIED -> UNMODIFIED
- */
- if (incomplete_data &&
- patch->ofile.file->mode == patch->nfile.file->mode &&
- patch->ofile.file->mode != GIT_FILEMODE_COMMIT &&
- git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id) &&
- patch->base.delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
- patch->base.delta->status = GIT_DELTA_UNMODIFIED;
-
-cleanup:
- patch_generated_update_binary(patch);
-
- if (!error) {
- if (patch_generated_diffable(patch))
- patch->flags |= GIT_PATCH_GENERATED_DIFFABLE;
-
- patch->flags |= GIT_PATCH_GENERATED_LOADED;
- }
-
- return error;
-}
-
-static int patch_generated_invoke_file_callback(
- git_patch_generated *patch, git_patch_generated_output *output)
-{
- float progress = patch->diff ?
- ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f;
-
- if (!output->file_cb)
- return 0;
-
- return giterr_set_after_callback_function(
- output->file_cb(patch->base.delta, progress, output->payload),
- "git_patch");
-}
-
-static int create_binary(
- git_diff_binary_t *out_type,
- char **out_data,
- size_t *out_datalen,
- size_t *out_inflatedlen,
- const char *a_data,
- size_t a_datalen,
- const char *b_data,
- size_t b_datalen)
-{
- git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT;
- size_t delta_data_len = 0;
- int error;
-
- /* The git_delta function accepts unsigned long only */
- if (!git__is_ulong(a_datalen) || !git__is_ulong(b_datalen))
- return GIT_EBUFS;
-
- if ((error = git_zstream_deflatebuf(&deflate, b_data, b_datalen)) < 0)
- goto done;
-
- /* The git_delta function accepts unsigned long only */
- if (!git__is_ulong(deflate.size)) {
- error = GIT_EBUFS;
- goto done;
- }
-
- if (a_datalen && b_datalen) {
- void *delta_data;
-
- error = git_delta(&delta_data, &delta_data_len,
- a_data, a_datalen,
- b_data, b_datalen,
- deflate.size);
-
- if (error == 0) {
- error = git_zstream_deflatebuf(
- &delta, delta_data, delta_data_len);
-
- git__free(delta_data);
- } else if (error == GIT_EBUFS) {
- error = 0;
- }
-
- if (error < 0)
- goto done;
- }
-
- if (delta.size && delta.size < deflate.size) {
- *out_type = GIT_DIFF_BINARY_DELTA;
- *out_datalen = delta.size;
- *out_data = git_buf_detach(&delta);
- *out_inflatedlen = delta_data_len;
- } else {
- *out_type = GIT_DIFF_BINARY_LITERAL;
- *out_datalen = deflate.size;
- *out_data = git_buf_detach(&deflate);
- *out_inflatedlen = b_datalen;
- }
-
-done:
- git_buf_free(&deflate);
- git_buf_free(&delta);
-
- return error;
-}
-
-static int diff_binary(git_patch_generated_output *output, git_patch_generated *patch)
-{
- git_diff_binary binary = {0};
- const char *old_data = patch->ofile.map.data;
- const char *new_data = patch->nfile.map.data;
- size_t old_len = patch->ofile.map.len,
- new_len = patch->nfile.map.len;
- int error;
-
- /* Only load contents if the user actually wants to diff
- * binary files. */
- if (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) {
- binary.contains_data = 1;
-
- /* Create the old->new delta (as the "new" side of the patch),
- * and the new->old delta (as the "old" side)
- */
- if ((error = create_binary(&binary.old_file.type,
- (char **)&binary.old_file.data,
- &binary.old_file.datalen,
- &binary.old_file.inflatedlen,
- new_data, new_len, old_data, old_len)) < 0 ||
- (error = create_binary(&binary.new_file.type,
- (char **)&binary.new_file.data,
- &binary.new_file.datalen,
- &binary.new_file.inflatedlen,
- old_data, old_len, new_data, new_len)) < 0)
- return error;
- }
-
- error = giterr_set_after_callback_function(
- output->binary_cb(patch->base.delta, &binary, output->payload),
- "git_patch");
-
- git__free((char *) binary.old_file.data);
- git__free((char *) binary.new_file.data);
-
- return error;
-}
-
-static int patch_generated_create(
- git_patch_generated *patch,
- git_patch_generated_output *output)
-{
- int error = 0;
-
- if ((patch->flags & GIT_PATCH_GENERATED_DIFFED) != 0)
- return 0;
-
- /* if we are not looking at the binary or text data, don't do the diff */
- if (!output->binary_cb && !output->hunk_cb && !output->data_cb)
- return 0;
-
- if ((patch->flags & GIT_PATCH_GENERATED_LOADED) == 0 &&
- (error = patch_generated_load(patch, output)) < 0)
- return error;
-
- if ((patch->flags & GIT_PATCH_GENERATED_DIFFABLE) == 0)
- return 0;
-
- if ((patch->base.delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
- if (output->binary_cb)
- error = diff_binary(output, patch);
- }
- else {
- if (output->diff_cb)
- error = output->diff_cb(output, patch);
- }
-
- patch->flags |= GIT_PATCH_GENERATED_DIFFED;
- return error;
-}
-
-static int diff_required(git_diff *diff, const char *action)
-{
- if (diff)
- return 0;
- giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action);
- return -1;
-}
-
-int git_diff_foreach(
- git_diff *diff,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb data_cb,
- void *payload)
-{
- int error = 0;
- git_xdiff_output xo;
- size_t idx;
- git_patch_generated patch;
-
- if ((error = diff_required(diff, "git_diff_foreach")) < 0)
- return error;
-
- memset(&xo, 0, sizeof(xo));
- memset(&patch, 0, sizeof(patch));
- diff_output_init(
- &xo.output, &diff->opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
- git_xdiff_init(&xo, &diff->opts);
-
- git_vector_foreach(&diff->deltas, idx, patch.base.delta) {
-
- /* check flags against patch status */
- if (git_diff_delta__should_skip(&diff->opts, patch.base.delta))
- continue;
-
- if (binary_cb || hunk_cb || data_cb) {
- if ((error = patch_generated_init(&patch, diff, idx)) != 0 ||
- (error = patch_generated_load(&patch, &xo.output)) != 0)
- return error;
- }
-
- if ((error = patch_generated_invoke_file_callback(&patch, &xo.output)) == 0) {
- if (binary_cb || hunk_cb || data_cb)
- error = patch_generated_create(&patch, &xo.output);
- }
-
- git_patch_free(&patch.base);
-
- if (error)
- break;
- }
-
- return error;
-}
-
-typedef struct {
- git_patch_generated patch;
- git_diff_delta delta;
- char paths[GIT_FLEX_ARRAY];
-} patch_generated_with_delta;
-
-static int diff_single_generate(patch_generated_with_delta *pd, git_xdiff_output *xo)
-{
- int error = 0;
- git_patch_generated *patch = &pd->patch;
- bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
- bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
-
- pd->delta.status = has_new ?
- (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
- (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
-
- if (git_oid_equal(&patch->nfile.file->id, &patch->ofile.file->id))
- pd->delta.status = GIT_DELTA_UNMODIFIED;
-
- patch->base.delta = &pd->delta;
-
- patch_generated_init_common(patch);
-
- if (pd->delta.status == GIT_DELTA_UNMODIFIED &&
- !(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED)) {
-
- /* Even empty patches are flagged as binary, and even though
- * there's no difference, we flag this as "containing data"
- * (the data is known to be empty, as opposed to wholly unknown).
- */
- if (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY)
- patch->base.binary.contains_data = 1;
-
- return error;
- }
-
- error = patch_generated_invoke_file_callback(patch, (git_patch_generated_output *)xo);
-
- if (!error)
- error = patch_generated_create(patch, (git_patch_generated_output *)xo);
-
- return error;
-}
-
-static int patch_generated_from_sources(
- patch_generated_with_delta *pd,
- git_xdiff_output *xo,
- git_diff_file_content_src *oldsrc,
- git_diff_file_content_src *newsrc,
- const git_diff_options *opts)
-{
- int error = 0;
- git_repository *repo =
- oldsrc->blob ? git_blob_owner(oldsrc->blob) :
- newsrc->blob ? git_blob_owner(newsrc->blob) : NULL;
- git_diff_file *lfile = &pd->delta.old_file, *rfile = &pd->delta.new_file;
- git_diff_file_content *ldata = &pd->patch.ofile, *rdata = &pd->patch.nfile;
-
- if ((error = patch_generated_normalize_options(&pd->patch.base.diff_opts, opts)) < 0)
- return error;
-
- if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
- void *tmp = lfile; lfile = rfile; rfile = tmp;
- tmp = ldata; ldata = rdata; rdata = tmp;
- }
-
- pd->patch.base.delta = &pd->delta;
-
- if (!oldsrc->as_path) {
- if (newsrc->as_path)
- oldsrc->as_path = newsrc->as_path;
- else
- oldsrc->as_path = newsrc->as_path = "file";
- }
- else if (!newsrc->as_path)
- newsrc->as_path = oldsrc->as_path;
-
- lfile->path = oldsrc->as_path;
- rfile->path = newsrc->as_path;
-
- if ((error = git_diff_file_content__init_from_src(
- ldata, repo, opts, oldsrc, lfile)) < 0 ||
- (error = git_diff_file_content__init_from_src(
- rdata, repo, opts, newsrc, rfile)) < 0)
- return error;
-
- return diff_single_generate(pd, xo);
-}
-
-static int patch_generated_with_delta_alloc(
- patch_generated_with_delta **out,
- const char **old_path,
- const char **new_path)
-{
- patch_generated_with_delta *pd;
- size_t old_len = *old_path ? strlen(*old_path) : 0;
- size_t new_len = *new_path ? strlen(*new_path) : 0;
- size_t alloc_len;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*pd), old_len);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, new_len);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
-
- *out = pd = git__calloc(1, alloc_len);
- GITERR_CHECK_ALLOC(pd);
-
- pd->patch.flags = GIT_PATCH_GENERATED_ALLOCATED;
-
- if (*old_path) {
- memcpy(&pd->paths[0], *old_path, old_len);
- *old_path = &pd->paths[0];
- } else if (*new_path)
- *old_path = &pd->paths[old_len + 1];
-
- if (*new_path) {
- memcpy(&pd->paths[old_len + 1], *new_path, new_len);
- *new_path = &pd->paths[old_len + 1];
- } else if (*old_path)
- *new_path = &pd->paths[0];
-
- return 0;
-}
-
-static int diff_from_sources(
- git_diff_file_content_src *oldsrc,
- git_diff_file_content_src *newsrc,
- const git_diff_options *opts,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb data_cb,
- void *payload)
-{
- int error = 0;
- patch_generated_with_delta pd;
- git_xdiff_output xo;
-
- memset(&xo, 0, sizeof(xo));
- diff_output_init(
- &xo.output, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
- git_xdiff_init(&xo, opts);
-
- memset(&pd, 0, sizeof(pd));
-
- error = patch_generated_from_sources(&pd, &xo, oldsrc, newsrc, opts);
-
- git_patch_free(&pd.patch.base);
-
- return error;
-}
-
-static int patch_from_sources(
- git_patch **out,
- git_diff_file_content_src *oldsrc,
- git_diff_file_content_src *newsrc,
- const git_diff_options *opts)
-{
- int error = 0;
- patch_generated_with_delta *pd;
- git_xdiff_output xo;
-
- assert(out);
- *out = NULL;
-
- if ((error = patch_generated_with_delta_alloc(
- &pd, &oldsrc->as_path, &newsrc->as_path)) < 0)
- return error;
-
- memset(&xo, 0, sizeof(xo));
- diff_output_to_patch(&xo.output, &pd->patch);
- git_xdiff_init(&xo, opts);
-
- if (!(error = patch_generated_from_sources(pd, &xo, oldsrc, newsrc, opts)))
- *out = (git_patch *)pd;
- else
- git_patch_free((git_patch *)pd);
-
- return error;
-}
-
-int git_diff_blobs(
- const git_blob *old_blob,
- const char *old_path,
- const git_blob *new_blob,
- const char *new_path,
- const git_diff_options *opts,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb data_cb,
- void *payload)
-{
- git_diff_file_content_src osrc =
- GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
- git_diff_file_content_src nsrc =
- GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path);
- return diff_from_sources(
- &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
-}
-
-int git_patch_from_blobs(
- git_patch **out,
- const git_blob *old_blob,
- const char *old_path,
- const git_blob *new_blob,
- const char *new_path,
- const git_diff_options *opts)
-{
- git_diff_file_content_src osrc =
- GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
- git_diff_file_content_src nsrc =
- GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path);
- return patch_from_sources(out, &osrc, &nsrc, opts);
-}
-
-int git_diff_blob_to_buffer(
- const git_blob *old_blob,
- const char *old_path,
- const char *buf,
- size_t buflen,
- const char *buf_path,
- const git_diff_options *opts,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb data_cb,
- void *payload)
-{
- git_diff_file_content_src osrc =
- GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
- git_diff_file_content_src nsrc =
- GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path);
- return diff_from_sources(
- &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
-}
-
-int git_patch_from_blob_and_buffer(
- git_patch **out,
- const git_blob *old_blob,
- const char *old_path,
- const char *buf,
- size_t buflen,
- const char *buf_path,
- const git_diff_options *opts)
-{
- git_diff_file_content_src osrc =
- GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
- git_diff_file_content_src nsrc =
- GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path);
- return patch_from_sources(out, &osrc, &nsrc, opts);
-}
-
-int git_diff_buffers(
- const void *old_buf,
- size_t old_len,
- const char *old_path,
- const void *new_buf,
- size_t new_len,
- const char *new_path,
- const git_diff_options *opts,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb data_cb,
- void *payload)
-{
- git_diff_file_content_src osrc =
- GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path);
- git_diff_file_content_src nsrc =
- GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path);
- return diff_from_sources(
- &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
-}
-
-int git_patch_from_buffers(
- git_patch **out,
- const void *old_buf,
- size_t old_len,
- const char *old_path,
- const char *new_buf,
- size_t new_len,
- const char *new_path,
- const git_diff_options *opts)
-{
- git_diff_file_content_src osrc =
- GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path);
- git_diff_file_content_src nsrc =
- GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path);
- return patch_from_sources(out, &osrc, &nsrc, opts);
-}
-
-int git_patch_generated_from_diff(
- git_patch **patch_ptr, git_diff *diff, size_t idx)
-{
- int error = 0;
- git_xdiff_output xo;
- git_diff_delta *delta = NULL;
- git_patch_generated *patch = NULL;
-
- if (patch_ptr) *patch_ptr = NULL;
-
- if (diff_required(diff, "git_patch_from_diff") < 0)
- return -1;
-
- delta = git_vector_get(&diff->deltas, idx);
- if (!delta) {
- giterr_set(GITERR_INVALID, "Index out of range for delta in diff");
- return GIT_ENOTFOUND;
- }
-
- if (git_diff_delta__should_skip(&diff->opts, delta))
- return 0;
-
- /* don't load the patch data unless we need it for binary check */
- if (!patch_ptr &&
- ((delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0 ||
- (diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0))
- return 0;
-
- if ((error = patch_generated_alloc_from_diff(&patch, diff, idx)) < 0)
- return error;
-
- memset(&xo, 0, sizeof(xo));
- diff_output_to_patch(&xo.output, patch);
- git_xdiff_init(&xo, &diff->opts);
-
- error = patch_generated_invoke_file_callback(patch, &xo.output);
-
- if (!error)
- error = patch_generated_create(patch, &xo.output);
-
- if (!error) {
- /* TODO: if cumulative diff size is < 0.5 total size, flatten patch */
- /* TODO: and unload the file content */
- }
-
- if (error || !patch_ptr)
- git_patch_free(&patch->base);
- else
- *patch_ptr = &patch->base;
-
- return error;
-}
-
-git_diff_driver *git_patch_generated_driver(git_patch_generated *patch)
-{
- /* ofile driver is representative for whole patch */
- return patch->ofile.driver;
-}
-
-void git_patch_generated_old_data(
- char **ptr, size_t *len, git_patch_generated *patch)
-{
- *ptr = patch->ofile.map.data;
- *len = patch->ofile.map.len;
-}
-
-void git_patch_generated_new_data(
- char **ptr, size_t *len, git_patch_generated *patch)
-{
- *ptr = patch->nfile.map.data;
- *len = patch->nfile.map.len;
-}
-
-static int patch_generated_file_cb(
- const git_diff_delta *delta,
- float progress,
- void *payload)
-{
- GIT_UNUSED(delta); GIT_UNUSED(progress); GIT_UNUSED(payload);
- return 0;
-}
-
-static int patch_generated_binary_cb(
- const git_diff_delta *delta,
- const git_diff_binary *binary,
- void *payload)
-{
- git_patch *patch = payload;
-
- GIT_UNUSED(delta);
-
- memcpy(&patch->binary, binary, sizeof(git_diff_binary));
-
- if (binary->old_file.data) {
- patch->binary.old_file.data = git__malloc(binary->old_file.datalen);
- GITERR_CHECK_ALLOC(patch->binary.old_file.data);
-
- memcpy((char *)patch->binary.old_file.data,
- binary->old_file.data, binary->old_file.datalen);
- }
-
- if (binary->new_file.data) {
- patch->binary.new_file.data = git__malloc(binary->new_file.datalen);
- GITERR_CHECK_ALLOC(patch->binary.new_file.data);
-
- memcpy((char *)patch->binary.new_file.data,
- binary->new_file.data, binary->new_file.datalen);
- }
-
- return 0;
-}
-
-static int git_patch_hunk_cb(
- const git_diff_delta *delta,
- const git_diff_hunk *hunk_,
- void *payload)
-{
- git_patch_generated *patch = payload;
- git_patch_hunk *hunk;
-
- GIT_UNUSED(delta);
-
- hunk = git_array_alloc(patch->base.hunks);
- GITERR_CHECK_ALLOC(hunk);
-
- memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk));
-
- patch->base.header_size += hunk_->header_len;
-
- hunk->line_start = git_array_size(patch->base.lines);
- hunk->line_count = 0;
-
- return 0;
-}
-
-static int patch_generated_line_cb(
- const git_diff_delta *delta,
- const git_diff_hunk *hunk_,
- const git_diff_line *line_,
- void *payload)
-{
- git_patch_generated *patch = payload;
- git_patch_hunk *hunk;
- git_diff_line *line;
-
- GIT_UNUSED(delta);
- GIT_UNUSED(hunk_);
-
- hunk = git_array_last(patch->base.hunks);
- assert(hunk); /* programmer error if no hunk is available */
-
- line = git_array_alloc(patch->base.lines);
- GITERR_CHECK_ALLOC(line);
-
- memcpy(line, line_, sizeof(*line));
-
- /* do some bookkeeping so we can provide old/new line numbers */
-
- patch->base.content_size += line->content_len;
-
- if (line->origin == GIT_DIFF_LINE_ADDITION ||
- line->origin == GIT_DIFF_LINE_DELETION)
- patch->base.content_size += 1;
- else if (line->origin == GIT_DIFF_LINE_CONTEXT) {
- patch->base.content_size += 1;
- patch->base.context_size += line->content_len + 1;
- } else if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
- patch->base.context_size += line->content_len;
-
- hunk->line_count++;
-
- return 0;
-}
-
-static void diff_output_init(
- git_patch_generated_output *out,
- const git_diff_options *opts,
- git_diff_file_cb file_cb,
- git_diff_binary_cb binary_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_line_cb data_cb,
- void *payload)
-{
- GIT_UNUSED(opts);
-
- memset(out, 0, sizeof(*out));
-
- out->file_cb = file_cb;
- out->binary_cb = binary_cb;
- out->hunk_cb = hunk_cb;
- out->data_cb = data_cb;
- out->payload = payload;
-}
-
-static void diff_output_to_patch(
- git_patch_generated_output *out, git_patch_generated *patch)
-{
- diff_output_init(
- out,
- NULL,
- patch_generated_file_cb,
- patch_generated_binary_cb,
- git_patch_hunk_cb,
- patch_generated_line_cb,
- patch);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_patch_generate_h__
-#define INCLUDE_patch_generate_h__
-
-#include "common.h"
-#include "diff.h"
-#include "diff_file.h"
-#include "patch.h"
-
-enum {
- GIT_PATCH_GENERATED_ALLOCATED = (1 << 0),
- GIT_PATCH_GENERATED_INITIALIZED = (1 << 1),
- GIT_PATCH_GENERATED_LOADED = (1 << 2),
- /* the two sides are different */
- GIT_PATCH_GENERATED_DIFFABLE = (1 << 3),
- /* the difference between the two sides has been computed */
- GIT_PATCH_GENERATED_DIFFED = (1 << 4),
- GIT_PATCH_GENERATED_FLATTENED = (1 << 5),
-};
-
-struct git_patch_generated {
- struct git_patch base;
-
- git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
- size_t delta_index;
- git_diff_file_content ofile;
- git_diff_file_content nfile;
- uint32_t flags;
- git_pool flattened;
-};
-
-typedef struct git_patch_generated git_patch_generated;
-
-extern git_diff_driver *git_patch_generated_driver(git_patch_generated *);
-
-extern void git_patch_generated_old_data(
- char **, size_t *, git_patch_generated *);
-extern void git_patch_generated_new_data(
- char **, size_t *, git_patch_generated *);
-extern int git_patch_generated_from_diff(
- git_patch **, git_diff *, size_t);
-
-typedef struct git_patch_generated_output git_patch_generated_output;
-
-struct git_patch_generated_output {
- /* these callbacks are issued with the diff data */
- git_diff_file_cb file_cb;
- git_diff_binary_cb binary_cb;
- git_diff_hunk_cb hunk_cb;
- git_diff_line_cb data_cb;
- void *payload;
-
- /* this records the actual error in cases where it may be obscured */
- int error;
-
- /* this callback is used to do the diff and drive the other callbacks.
- * see diff_xdiff.h for how to use this in practice for now.
- */
- int (*diff_cb)(git_patch_generated_output *output,
- git_patch_generated *patch);
-};
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "git2/patch.h"
-#include "patch.h"
-#include "patch_parse.h"
-#include "diff_parse.h"
-#include "path.h"
-
-#define parse_err(...) \
- ( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
-
-typedef struct {
- git_patch base;
-
- git_patch_parse_ctx *ctx;
-
- /* the paths from the `diff --git` header, these will be used if this is not
- * a rename (and rename paths are specified) or if no `+++`/`---` line specify
- * the paths.
- */
- char *header_old_path, *header_new_path;
-
- /* renamed paths are precise and are not prefixed */
- char *rename_old_path, *rename_new_path;
-
- /* the paths given in `---` and `+++` lines */
- char *old_path, *new_path;
-
- /* the prefixes from the old/new paths */
- char *old_prefix, *new_prefix;
-} git_patch_parsed;
-
-
-GIT_INLINE(bool) parse_ctx_contains(
- git_patch_parse_ctx *ctx, const char *str, size_t len)
-{
- return (ctx->line_len >= len && memcmp(ctx->line, str, len) == 0);
-}
-
-#define parse_ctx_contains_s(ctx, str) \
- parse_ctx_contains(ctx, str, sizeof(str) - 1)
-
-static void parse_advance_line(git_patch_parse_ctx *ctx)
-{
- ctx->line += ctx->line_len;
- ctx->remain_len -= ctx->line_len;
- ctx->line_len = git__linenlen(ctx->line, ctx->remain_len);
- ctx->line_num++;
-}
-
-static void parse_advance_chars(git_patch_parse_ctx *ctx, size_t char_cnt)
-{
- ctx->line += char_cnt;
- ctx->remain_len -= char_cnt;
- ctx->line_len -= char_cnt;
-}
-
-static int parse_advance_expected(
- git_patch_parse_ctx *ctx,
- const char *expected,
- size_t expected_len)
-{
- if (ctx->line_len < expected_len)
- return -1;
-
- if (memcmp(ctx->line, expected, expected_len) != 0)
- return -1;
-
- parse_advance_chars(ctx, expected_len);
- return 0;
-}
-
-#define parse_advance_expected_str(ctx, str) \
- parse_advance_expected(ctx, str, strlen(str))
-
-static int parse_advance_ws(git_patch_parse_ctx *ctx)
-{
- int ret = -1;
-
- while (ctx->line_len > 0 &&
- ctx->line[0] != '\n' &&
- git__isspace(ctx->line[0])) {
- ctx->line++;
- ctx->line_len--;
- ctx->remain_len--;
- ret = 0;
- }
-
- return ret;
-}
-
-static int parse_advance_nl(git_patch_parse_ctx *ctx)
-{
- if (ctx->line_len != 1 || ctx->line[0] != '\n')
- return -1;
-
- parse_advance_line(ctx);
- return 0;
-}
-
-static int header_path_len(git_patch_parse_ctx *ctx)
-{
- bool inquote = 0;
- bool quoted = (ctx->line_len > 0 && ctx->line[0] == '"');
- size_t len;
-
- for (len = quoted; len < ctx->line_len; len++) {
- if (!quoted && git__isspace(ctx->line[len]))
- break;
- else if (quoted && !inquote && ctx->line[len] == '"') {
- len++;
- break;
- }
-
- inquote = (!inquote && ctx->line[len] == '\\');
- }
-
- return len;
-}
-
-static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx)
-{
- int path_len, error = 0;
-
- path_len = header_path_len(ctx);
-
- if ((error = git_buf_put(path, ctx->line, path_len)) < 0)
- goto done;
-
- parse_advance_chars(ctx, path_len);
-
- git_buf_rtrim(path);
-
- if (path->size > 0 && path->ptr[0] == '"')
- error = git_buf_unquote(path);
-
- if (error < 0)
- goto done;
-
- git_path_squash_slashes(path);
-
-done:
- return error;
-}
-
-static int parse_header_path(char **out, git_patch_parse_ctx *ctx)
-{
- git_buf path = GIT_BUF_INIT;
- int error = parse_header_path_buf(&path, ctx);
-
- *out = git_buf_detach(&path);
-
- return error;
-}
-
-static int parse_header_git_oldpath(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- return parse_header_path(&patch->old_path, ctx);
-}
-
-static int parse_header_git_newpath(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- return parse_header_path(&patch->new_path, ctx);
-}
-
-static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx)
-{
- const char *end;
- int32_t m;
- int ret;
-
- if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]))
- return parse_err("invalid file mode at line %"PRIuZ, ctx->line_num);
-
- if ((ret = git__strntol32(&m, ctx->line, ctx->line_len, &end, 8)) < 0)
- return ret;
-
- if (m > UINT16_MAX)
- return -1;
-
- *mode = (uint16_t)m;
-
- parse_advance_chars(ctx, (end - ctx->line));
-
- return ret;
-}
-
-static int parse_header_oid(
- git_oid *oid,
- uint16_t *oid_len,
- git_patch_parse_ctx *ctx)
-{
- size_t len;
-
- for (len = 0; len < ctx->line_len && len < GIT_OID_HEXSZ; len++) {
- if (!git__isxdigit(ctx->line[len]))
- break;
- }
-
- if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ ||
- git_oid_fromstrn(oid, ctx->line, len) < 0)
- return parse_err("invalid hex formatted object id at line %"PRIuZ,
- ctx->line_num);
-
- parse_advance_chars(ctx, len);
-
- *oid_len = (uint16_t)len;
-
- return 0;
-}
-
-static int parse_header_git_index(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- if (parse_header_oid(&patch->base.delta->old_file.id,
- &patch->base.delta->old_file.id_abbrev, ctx) < 0 ||
- parse_advance_expected_str(ctx, "..") < 0 ||
- parse_header_oid(&patch->base.delta->new_file.id,
- &patch->base.delta->new_file.id_abbrev, ctx) < 0)
- return -1;
-
- if (ctx->line_len > 0 && ctx->line[0] == ' ') {
- uint16_t mode;
-
- parse_advance_chars(ctx, 1);
-
- if (parse_header_mode(&mode, ctx) < 0)
- return -1;
-
- if (!patch->base.delta->new_file.mode)
- patch->base.delta->new_file.mode = mode;
-
- if (!patch->base.delta->old_file.mode)
- patch->base.delta->old_file.mode = mode;
- }
-
- return 0;
-}
-
-static int parse_header_git_oldmode(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- return parse_header_mode(&patch->base.delta->old_file.mode, ctx);
-}
-
-static int parse_header_git_newmode(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- return parse_header_mode(&patch->base.delta->new_file.mode, ctx);
-}
-
-static int parse_header_git_deletedfilemode(
- git_patch_parsed *patch,
- git_patch_parse_ctx *ctx)
-{
- git__free((char *)patch->base.delta->old_file.path);
-
- patch->base.delta->old_file.path = NULL;
- patch->base.delta->status = GIT_DELTA_DELETED;
- patch->base.delta->nfiles = 1;
-
- return parse_header_mode(&patch->base.delta->old_file.mode, ctx);
-}
-
-static int parse_header_git_newfilemode(
- git_patch_parsed *patch,
- git_patch_parse_ctx *ctx)
-{
- git__free((char *)patch->base.delta->new_file.path);
-
- patch->base.delta->new_file.path = NULL;
- patch->base.delta->status = GIT_DELTA_ADDED;
- patch->base.delta->nfiles = 1;
-
- return parse_header_mode(&patch->base.delta->new_file.mode, ctx);
-}
-
-static int parse_header_rename(
- char **out,
- git_patch_parse_ctx *ctx)
-{
- git_buf path = GIT_BUF_INIT;
-
- if (parse_header_path_buf(&path, ctx) < 0)
- return -1;
-
- /* Note: the `rename from` and `rename to` lines include the literal
- * filename. They do *not* include the prefix. (Who needs consistency?)
- */
- *out = git_buf_detach(&path);
- return 0;
-}
-
-static int parse_header_renamefrom(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- patch->base.delta->status = GIT_DELTA_RENAMED;
- return parse_header_rename(&patch->rename_old_path, ctx);
-}
-
-static int parse_header_renameto(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- patch->base.delta->status = GIT_DELTA_RENAMED;
- return parse_header_rename(&patch->rename_new_path, ctx);
-}
-
-static int parse_header_copyfrom(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- patch->base.delta->status = GIT_DELTA_COPIED;
- return parse_header_rename(&patch->rename_old_path, ctx);
-}
-
-static int parse_header_copyto(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- patch->base.delta->status = GIT_DELTA_COPIED;
- return parse_header_rename(&patch->rename_new_path, ctx);
-}
-
-static int parse_header_percent(uint16_t *out, git_patch_parse_ctx *ctx)
-{
- int32_t val;
- const char *end;
-
- if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]) ||
- git__strntol32(&val, ctx->line, ctx->line_len, &end, 10) < 0)
- return -1;
-
- parse_advance_chars(ctx, (end - ctx->line));
-
- if (parse_advance_expected_str(ctx, "%") < 0)
- return -1;
-
- if (val > 100)
- return -1;
-
- *out = val;
- return 0;
-}
-
-static int parse_header_similarity(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- if (parse_header_percent(&patch->base.delta->similarity, ctx) < 0)
- return parse_err("invalid similarity percentage at line %"PRIuZ,
- ctx->line_num);
-
- return 0;
-}
-
-static int parse_header_dissimilarity(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- uint16_t dissimilarity;
-
- if (parse_header_percent(&dissimilarity, ctx) < 0)
- return parse_err("invalid similarity percentage at line %"PRIuZ,
- ctx->line_num);
-
- patch->base.delta->similarity = 100 - dissimilarity;
-
- return 0;
-}
-
-typedef struct {
- const char *str;
- int(*fn)(git_patch_parsed *, git_patch_parse_ctx *);
-} header_git_op;
-
-static const header_git_op header_git_ops[] = {
- { "diff --git ", NULL },
- { "@@ -", NULL },
- { "GIT binary patch", NULL },
- { "Binary files ", NULL },
- { "--- ", parse_header_git_oldpath },
- { "+++ ", parse_header_git_newpath },
- { "index ", parse_header_git_index },
- { "old mode ", parse_header_git_oldmode },
- { "new mode ", parse_header_git_newmode },
- { "deleted file mode ", parse_header_git_deletedfilemode },
- { "new file mode ", parse_header_git_newfilemode },
- { "rename from ", parse_header_renamefrom },
- { "rename to ", parse_header_renameto },
- { "rename old ", parse_header_renamefrom },
- { "rename new ", parse_header_renameto },
- { "copy from ", parse_header_copyfrom },
- { "copy to ", parse_header_copyto },
- { "similarity index ", parse_header_similarity },
- { "dissimilarity index ", parse_header_dissimilarity },
-};
-
-static int parse_header_git(
- git_patch_parsed *patch,
- git_patch_parse_ctx *ctx)
-{
- size_t i;
- int error = 0;
-
- /* Parse the diff --git line */
- if (parse_advance_expected_str(ctx, "diff --git ") < 0)
- return parse_err("corrupt git diff header at line %"PRIuZ, ctx->line_num);
-
- if (parse_header_path(&patch->header_old_path, ctx) < 0)
- return parse_err("corrupt old path in git diff header at line %"PRIuZ,
- ctx->line_num);
-
- if (parse_advance_ws(ctx) < 0 ||
- parse_header_path(&patch->header_new_path, ctx) < 0)
- return parse_err("corrupt new path in git diff header at line %"PRIuZ,
- ctx->line_num);
-
- /* Parse remaining header lines */
- for (parse_advance_line(ctx);
- ctx->remain_len > 0;
- parse_advance_line(ctx)) {
-
- bool found = false;
-
- if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n')
- break;
-
- for (i = 0; i < ARRAY_SIZE(header_git_ops); i++) {
- const header_git_op *op = &header_git_ops[i];
- size_t op_len = strlen(op->str);
-
- if (memcmp(ctx->line, op->str, min(op_len, ctx->line_len)) != 0)
- continue;
-
- /* Do not advance if this is the patch separator */
- if (op->fn == NULL)
- goto done;
-
- parse_advance_chars(ctx, op_len);
-
- if ((error = op->fn(patch, ctx)) < 0)
- goto done;
-
- parse_advance_ws(ctx);
- parse_advance_expected_str(ctx, "\n");
-
- if (ctx->line_len > 0) {
- error = parse_err("trailing data at line %"PRIuZ, ctx->line_num);
- goto done;
- }
-
- found = true;
- break;
- }
-
- if (!found) {
- error = parse_err("invalid patch header at line %"PRIuZ,
- ctx->line_num);
- goto done;
- }
- }
-
-done:
- return error;
-}
-
-static int parse_number(git_off_t *out, git_patch_parse_ctx *ctx)
-{
- const char *end;
- int64_t num;
-
- if (!git__isdigit(ctx->line[0]))
- return -1;
-
- if (git__strntol64(&num, ctx->line, ctx->line_len, &end, 10) < 0)
- return -1;
-
- if (num < 0)
- return -1;
-
- *out = num;
- parse_advance_chars(ctx, (end - ctx->line));
-
- return 0;
-}
-
-static int parse_int(int *out, git_patch_parse_ctx *ctx)
-{
- git_off_t num;
-
- if (parse_number(&num, ctx) < 0 || !git__is_int(num))
- return -1;
-
- *out = (int)num;
- return 0;
-}
-
-static int parse_hunk_header(
- git_patch_hunk *hunk,
- git_patch_parse_ctx *ctx)
-{
- const char *header_start = ctx->line;
-
- hunk->hunk.old_lines = 1;
- hunk->hunk.new_lines = 1;
-
- if (parse_advance_expected_str(ctx, "@@ -") < 0 ||
- parse_int(&hunk->hunk.old_start, ctx) < 0)
- goto fail;
-
- if (ctx->line_len > 0 && ctx->line[0] == ',') {
- if (parse_advance_expected_str(ctx, ",") < 0 ||
- parse_int(&hunk->hunk.old_lines, ctx) < 0)
- goto fail;
- }
-
- if (parse_advance_expected_str(ctx, " +") < 0 ||
- parse_int(&hunk->hunk.new_start, ctx) < 0)
- goto fail;
-
- if (ctx->line_len > 0 && ctx->line[0] == ',') {
- if (parse_advance_expected_str(ctx, ",") < 0 ||
- parse_int(&hunk->hunk.new_lines, ctx) < 0)
- goto fail;
- }
-
- if (parse_advance_expected_str(ctx, " @@") < 0)
- goto fail;
-
- parse_advance_line(ctx);
-
- if (!hunk->hunk.old_lines && !hunk->hunk.new_lines)
- goto fail;
-
- hunk->hunk.header_len = ctx->line - header_start;
- if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1))
- return parse_err("oversized patch hunk header at line %"PRIuZ,
- ctx->line_num);
-
- memcpy(hunk->hunk.header, header_start, hunk->hunk.header_len);
- hunk->hunk.header[hunk->hunk.header_len] = '\0';
-
- return 0;
-
-fail:
- giterr_set(GITERR_PATCH, "invalid patch hunk header at line %"PRIuZ,
- ctx->line_num);
- return -1;
-}
-
-static int parse_hunk_body(
- git_patch_parsed *patch,
- git_patch_hunk *hunk,
- git_patch_parse_ctx *ctx)
-{
- git_diff_line *line;
- int error = 0;
-
- int oldlines = hunk->hunk.old_lines;
- int newlines = hunk->hunk.new_lines;
-
- for (;
- ctx->remain_len > 4 && (oldlines || newlines) &&
- memcmp(ctx->line, "@@ -", 4) != 0;
- parse_advance_line(ctx)) {
-
- int origin;
- int prefix = 1;
-
- if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n') {
- error = parse_err("invalid patch instruction at line %"PRIuZ,
- ctx->line_num);
- goto done;
- }
-
- switch (ctx->line[0]) {
- case '\n':
- prefix = 0;
-
- case ' ':
- origin = GIT_DIFF_LINE_CONTEXT;
- oldlines--;
- newlines--;
- break;
-
- case '-':
- origin = GIT_DIFF_LINE_DELETION;
- oldlines--;
- break;
-
- case '+':
- origin = GIT_DIFF_LINE_ADDITION;
- newlines--;
- break;
-
- default:
- error = parse_err("invalid patch hunk at line %"PRIuZ, ctx->line_num);
- goto done;
- }
-
- line = git_array_alloc(patch->base.lines);
- GITERR_CHECK_ALLOC(line);
-
- memset(line, 0x0, sizeof(git_diff_line));
-
- line->content = ctx->line + prefix;
- line->content_len = ctx->line_len - prefix;
- line->content_offset = ctx->content_len - ctx->remain_len;
- line->origin = origin;
-
- hunk->line_count++;
- }
-
- if (oldlines || newlines) {
- error = parse_err(
- "invalid patch hunk, expected %d old lines and %d new lines",
- hunk->hunk.old_lines, hunk->hunk.new_lines);
- goto done;
- }
-
- /* Handle "\ No newline at end of file". Only expect the leading
- * backslash, though, because the rest of the string could be
- * localized. Because `diff` optimizes for the case where you
- * want to apply the patch by hand.
- */
- if (parse_ctx_contains_s(ctx, "\\ ") &&
- git_array_size(patch->base.lines) > 0) {
-
- line = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1);
-
- if (line->content_len < 1) {
- error = parse_err("cannot trim trailing newline of empty line");
- goto done;
- }
-
- line->content_len--;
-
- parse_advance_line(ctx);
- }
-
-done:
- return error;
-}
-
-static int parse_patch_header(
- git_patch_parsed *patch,
- git_patch_parse_ctx *ctx)
-{
- int error = 0;
-
- for (ctx->line = ctx->remain;
- ctx->remain_len > 0;
- parse_advance_line(ctx)) {
-
- /* This line is too short to be a patch header. */
- if (ctx->line_len < 6)
- continue;
-
- /* This might be a hunk header without a patch header, provide a
- * sensible error message. */
- if (parse_ctx_contains_s(ctx, "@@ -")) {
- size_t line_num = ctx->line_num;
- git_patch_hunk hunk;
-
- /* If this cannot be parsed as a hunk header, it's just leading
- * noise, continue.
- */
- if (parse_hunk_header(&hunk, ctx) < 0) {
- giterr_clear();
- continue;
- }
-
- error = parse_err("invalid hunk header outside patch at line %"PRIuZ,
- line_num);
- goto done;
- }
-
- /* This buffer is too short to contain a patch. */
- if (ctx->remain_len < ctx->line_len + 6)
- break;
-
- /* A proper git patch */
- if (parse_ctx_contains_s(ctx, "diff --git ")) {
- error = parse_header_git(patch, ctx);
- goto done;
- }
-
- error = 0;
- continue;
- }
-
- giterr_set(GITERR_PATCH, "no patch found");
- error = GIT_ENOTFOUND;
-
-done:
- return error;
-}
-
-static int parse_patch_binary_side(
- git_diff_binary_file *binary,
- git_patch_parse_ctx *ctx)
-{
- git_diff_binary_t type = GIT_DIFF_BINARY_NONE;
- git_buf base85 = GIT_BUF_INIT, decoded = GIT_BUF_INIT;
- git_off_t len;
- int error = 0;
-
- if (parse_ctx_contains_s(ctx, "literal ")) {
- type = GIT_DIFF_BINARY_LITERAL;
- parse_advance_chars(ctx, 8);
- } else if (parse_ctx_contains_s(ctx, "delta ")) {
- type = GIT_DIFF_BINARY_DELTA;
- parse_advance_chars(ctx, 6);
- } else {
- error = parse_err(
- "unknown binary delta type at line %"PRIuZ, ctx->line_num);
- goto done;
- }
-
- if (parse_number(&len, ctx) < 0 || parse_advance_nl(ctx) < 0 || len < 0) {
- error = parse_err("invalid binary size at line %"PRIuZ, ctx->line_num);
- goto done;
- }
-
- while (ctx->line_len) {
- char c = ctx->line[0];
- size_t encoded_len, decoded_len = 0, decoded_orig = decoded.size;
-
- if (c == '\n')
- break;
- else if (c >= 'A' && c <= 'Z')
- decoded_len = c - 'A' + 1;
- else if (c >= 'a' && c <= 'z')
- decoded_len = c - 'a' + (('z' - 'a') + 1) + 1;
-
- if (!decoded_len) {
- error = parse_err("invalid binary length at line %"PRIuZ, ctx->line_num);
- goto done;
- }
-
- parse_advance_chars(ctx, 1);
-
- encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5;
-
- if (encoded_len > ctx->line_len - 1) {
- error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num);
- goto done;
- }
-
- if ((error = git_buf_decode_base85(
- &decoded, ctx->line, encoded_len, decoded_len)) < 0)
- goto done;
-
- if (decoded.size - decoded_orig != decoded_len) {
- error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num);
- goto done;
- }
-
- parse_advance_chars(ctx, encoded_len);
-
- if (parse_advance_nl(ctx) < 0) {
- error = parse_err("trailing data at line %"PRIuZ, ctx->line_num);
- goto done;
- }
- }
-
- binary->type = type;
- binary->inflatedlen = (size_t)len;
- binary->datalen = decoded.size;
- binary->data = git_buf_detach(&decoded);
-
-done:
- git_buf_free(&base85);
- git_buf_free(&decoded);
- return error;
-}
-
-static int parse_patch_binary(
- git_patch_parsed *patch,
- git_patch_parse_ctx *ctx)
-{
- int error;
-
- if (parse_advance_expected_str(ctx, "GIT binary patch") < 0 ||
- parse_advance_nl(ctx) < 0)
- return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num);
-
- /* parse old->new binary diff */
- if ((error = parse_patch_binary_side(
- &patch->base.binary.new_file, ctx)) < 0)
- return error;
-
- if (parse_advance_nl(ctx) < 0)
- return parse_err("corrupt git binary separator at line %"PRIuZ,
- ctx->line_num);
-
- /* parse new->old binary diff */
- if ((error = parse_patch_binary_side(
- &patch->base.binary.old_file, ctx)) < 0)
- return error;
-
- if (parse_advance_nl(ctx) < 0)
- return parse_err("corrupt git binary patch separator at line %"PRIuZ,
- ctx->line_num);
-
- patch->base.binary.contains_data = 1;
- patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
- return 0;
-}
-
-static int parse_patch_binary_nodata(
- git_patch_parsed *patch,
- git_patch_parse_ctx *ctx)
-{
- if (parse_advance_expected_str(ctx, "Binary files ") < 0 ||
- parse_advance_expected_str(ctx, patch->header_old_path) < 0 ||
- parse_advance_expected_str(ctx, " and ") < 0 ||
- parse_advance_expected_str(ctx, patch->header_new_path) < 0 ||
- parse_advance_expected_str(ctx, " differ") < 0 ||
- parse_advance_nl(ctx) < 0)
- return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num);
-
- patch->base.binary.contains_data = 0;
- patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
- return 0;
-}
-
-static int parse_patch_hunks(
- git_patch_parsed *patch,
- git_patch_parse_ctx *ctx)
-{
- git_patch_hunk *hunk;
- int error = 0;
-
- while (parse_ctx_contains_s(ctx, "@@ -")) {
- hunk = git_array_alloc(patch->base.hunks);
- GITERR_CHECK_ALLOC(hunk);
-
- memset(hunk, 0, sizeof(git_patch_hunk));
-
- hunk->line_start = git_array_size(patch->base.lines);
- hunk->line_count = 0;
-
- if ((error = parse_hunk_header(hunk, ctx)) < 0 ||
- (error = parse_hunk_body(patch, hunk, ctx)) < 0)
- goto done;
- }
-
- patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
-
-done:
- return error;
-}
-
-static int parse_patch_body(
- git_patch_parsed *patch, git_patch_parse_ctx *ctx)
-{
- if (parse_ctx_contains_s(ctx, "GIT binary patch"))
- return parse_patch_binary(patch, ctx);
- else if (parse_ctx_contains_s(ctx, "Binary files "))
- return parse_patch_binary_nodata(patch, ctx);
- else
- return parse_patch_hunks(patch, ctx);
-}
-
-int check_header_names(
- const char *one,
- const char *two,
- const char *old_or_new,
- bool two_null)
-{
- if (!one || !two)
- return 0;
-
- if (two_null && strcmp(two, "/dev/null") != 0)
- return parse_err("expected %s path of '/dev/null'", old_or_new);
-
- else if (!two_null && strcmp(one, two) != 0)
- return parse_err("mismatched %s path names", old_or_new);
-
- return 0;
-}
-
-static int check_prefix(
- char **out,
- size_t *out_len,
- git_patch_parsed *patch,
- const char *path_start)
-{
- const char *path = path_start;
- size_t prefix_len = patch->ctx->opts.prefix_len;
- size_t remain_len = prefix_len;
-
- *out = NULL;
- *out_len = 0;
-
- if (prefix_len == 0)
- goto done;
-
- /* leading slashes do not count as part of the prefix in git apply */
- while (*path == '/')
- path++;
-
- while (*path && remain_len) {
- if (*path == '/')
- remain_len--;
-
- path++;
- }
-
- if (remain_len || !*path)
- return parse_err(
- "header filename does not contain %"PRIuZ" path components",
- prefix_len);
-
-done:
- *out_len = (path - path_start);
- *out = git__strndup(path_start, *out_len);
-
- return (*out == NULL) ? -1 : 0;
-}
-
-static int check_filenames(git_patch_parsed *patch)
-{
- const char *prefixed_new, *prefixed_old;
- size_t old_prefixlen = 0, new_prefixlen = 0;
- bool added = (patch->base.delta->status == GIT_DELTA_ADDED);
- bool deleted = (patch->base.delta->status == GIT_DELTA_DELETED);
-
- if (patch->old_path && !patch->new_path)
- return parse_err("missing new path");
-
- if (!patch->old_path && patch->new_path)
- return parse_err("missing old path");
-
- /* Ensure (non-renamed) paths match */
- if (check_header_names(
- patch->header_old_path, patch->old_path, "old", added) < 0 ||
- check_header_names(
- patch->header_new_path, patch->new_path, "new", deleted) < 0)
- return -1;
-
- prefixed_old = (!added && patch->old_path) ? patch->old_path :
- patch->header_old_path;
- prefixed_new = (!deleted && patch->new_path) ? patch->new_path :
- patch->header_new_path;
-
- if (check_prefix(
- &patch->old_prefix, &old_prefixlen, patch, prefixed_old) < 0 ||
- check_prefix(
- &patch->new_prefix, &new_prefixlen, patch, prefixed_new) < 0)
- return -1;
-
- /* Prefer the rename filenames as they are unambiguous and unprefixed */
- if (patch->rename_old_path)
- patch->base.delta->old_file.path = patch->rename_old_path;
- else
- patch->base.delta->old_file.path = prefixed_old + old_prefixlen;
-
- if (patch->rename_new_path)
- patch->base.delta->new_file.path = patch->rename_new_path;
- else
- patch->base.delta->new_file.path = prefixed_new + new_prefixlen;
-
- if (!patch->base.delta->old_file.path &&
- !patch->base.delta->new_file.path)
- return parse_err("git diff header lacks old / new paths");
-
- return 0;
-}
-
-static int check_patch(git_patch_parsed *patch)
-{
- git_diff_delta *delta = patch->base.delta;
-
- if (check_filenames(patch) < 0)
- return -1;
-
- if (delta->old_file.path &&
- delta->status != GIT_DELTA_DELETED &&
- !delta->new_file.mode)
- delta->new_file.mode = delta->old_file.mode;
-
- if (delta->status == GIT_DELTA_MODIFIED &&
- !(delta->flags & GIT_DIFF_FLAG_BINARY) &&
- delta->new_file.mode == delta->old_file.mode &&
- git_array_size(patch->base.hunks) == 0)
- return parse_err("patch with no hunks");
-
- if (delta->status == GIT_DELTA_ADDED) {
- memset(&delta->old_file.id, 0x0, sizeof(git_oid));
- delta->old_file.id_abbrev = 0;
- }
-
- if (delta->status == GIT_DELTA_DELETED) {
- memset(&delta->new_file.id, 0x0, sizeof(git_oid));
- delta->new_file.id_abbrev = 0;
- }
-
- return 0;
-}
-
-git_patch_parse_ctx *git_patch_parse_ctx_init(
- const char *content,
- size_t content_len,
- const git_patch_options *opts)
-{
- git_patch_parse_ctx *ctx;
- git_patch_options default_opts = GIT_PATCH_OPTIONS_INIT;
-
- if ((ctx = git__calloc(1, sizeof(git_patch_parse_ctx))) == NULL)
- return NULL;
-
- if (content_len) {
- if ((ctx->content = git__malloc(content_len)) == NULL) {
- git__free(ctx);
- return NULL;
- }
-
- memcpy((char *)ctx->content, content, content_len);
- }
-
- ctx->content_len = content_len;
- ctx->remain = ctx->content;
- ctx->remain_len = ctx->content_len;
-
- if (opts)
- memcpy(&ctx->opts, opts, sizeof(git_patch_options));
- else
- memcpy(&ctx->opts, &default_opts, sizeof(git_patch_options));
-
- GIT_REFCOUNT_INC(ctx);
- return ctx;
-}
-
-static void patch_parse_ctx_free(git_patch_parse_ctx *ctx)
-{
- if (!ctx)
- return;
-
- git__free((char *)ctx->content);
- git__free(ctx);
-}
-
-void git_patch_parse_ctx_free(git_patch_parse_ctx *ctx)
-{
- GIT_REFCOUNT_DEC(ctx, patch_parse_ctx_free);
-}
-
-int git_patch_parsed_from_diff(git_patch **out, git_diff *d, size_t idx)
-{
- git_diff_parsed *diff = (git_diff_parsed *)d;
- git_patch *p;
-
- if ((p = git_vector_get(&diff->patches, idx)) == NULL)
- return -1;
-
- GIT_REFCOUNT_INC(p);
- *out = p;
-
- return 0;
-}
-
-static void patch_parsed__free(git_patch *p)
-{
- git_patch_parsed *patch = (git_patch_parsed *)p;
-
- if (!patch)
- return;
-
- git_patch_parse_ctx_free(patch->ctx);
-
- git__free((char *)patch->base.binary.old_file.data);
- git__free((char *)patch->base.binary.new_file.data);
- git_array_clear(patch->base.hunks);
- git_array_clear(patch->base.lines);
- git__free(patch->base.delta);
-
- git__free(patch->old_prefix);
- git__free(patch->new_prefix);
- git__free(patch->header_old_path);
- git__free(patch->header_new_path);
- git__free(patch->rename_old_path);
- git__free(patch->rename_new_path);
- git__free(patch->old_path);
- git__free(patch->new_path);
- git__free(patch);
-}
-
-int git_patch_parse(
- git_patch **out,
- git_patch_parse_ctx *ctx)
-{
- git_patch_parsed *patch;
- size_t start, used;
- int error = 0;
-
- assert(out && ctx);
-
- *out = NULL;
-
- patch = git__calloc(1, sizeof(git_patch_parsed));
- GITERR_CHECK_ALLOC(patch);
-
- patch->ctx = ctx;
- GIT_REFCOUNT_INC(patch->ctx);
-
- patch->base.free_fn = patch_parsed__free;
-
- patch->base.delta = git__calloc(1, sizeof(git_diff_delta));
- GITERR_CHECK_ALLOC(patch->base.delta);
-
- patch->base.delta->status = GIT_DELTA_MODIFIED;
- patch->base.delta->nfiles = 2;
-
- start = ctx->remain_len;
-
- if ((error = parse_patch_header(patch, ctx)) < 0 ||
- (error = parse_patch_body(patch, ctx)) < 0 ||
- (error = check_patch(patch)) < 0)
- goto done;
-
- used = start - ctx->remain_len;
- ctx->remain += used;
-
- patch->base.diff_opts.old_prefix = patch->old_prefix;
- patch->base.diff_opts.new_prefix = patch->new_prefix;
- patch->base.diff_opts.flags |= GIT_DIFF_SHOW_BINARY;
-
- GIT_REFCOUNT_INC(patch);
- *out = &patch->base;
-
-done:
- if (error < 0)
- patch_parsed__free(&patch->base);
-
- return error;
-}
-
-int git_patch_from_buffer(
- git_patch **out,
- const char *content,
- size_t content_len,
- const git_patch_options *opts)
-{
- git_patch_parse_ctx *ctx;
- int error;
-
- ctx = git_patch_parse_ctx_init(content, content_len, opts);
- GITERR_CHECK_ALLOC(ctx);
-
- error = git_patch_parse(out, ctx);
-
- git_patch_parse_ctx_free(ctx);
- return error;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_patch_parse_h__
-#define INCLUDE_patch_parse_h__
-
-typedef struct {
- git_refcount rc;
-
- /* Original content buffer */
- const char *content;
- size_t content_len;
-
- git_patch_options opts;
-
- /* The remaining (unparsed) buffer */
- const char *remain;
- size_t remain_len;
-
- const char *line;
- size_t line_len;
- size_t line_num;
-} git_patch_parse_ctx;
-
-extern git_patch_parse_ctx *git_patch_parse_ctx_init(
- const char *content,
- size_t content_len,
- const git_patch_options *opts);
-
-extern void git_patch_parse_ctx_free(git_patch_parse_ctx *ctx);
-
-/**
- * Create a patch for a single file from the contents of a patch buffer.
- *
- * @param out The patch to be created
- * @param contents The contents of a patch file
- * @param contents_len The length of the patch file
- * @param opts The git_patch_options
- * @return 0 on success, <0 on failure.
- */
-extern int git_patch_from_buffer(
- git_patch **out,
- const char *contents,
- size_t contents_len,
- const git_patch_options *opts);
-
-extern int git_patch_parse(
- git_patch **out,
- git_patch_parse_ctx *ctx);
-
-extern int git_patch_parsed_from_diff(git_patch **, git_diff *, size_t);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "path.h"
-#include "posix.h"
-#include "repository.h"
-#ifdef GIT_WIN32
-#include "win32/posix.h"
-#include "win32/w32_buffer.h"
-#include "win32/w32_util.h"
-#include "win32/version.h"
-#else
-#include <dirent.h>
-#endif
-#include <stdio.h>
-#include <ctype.h>
-
-#define LOOKS_LIKE_DRIVE_PREFIX(S) (git__isalpha((S)[0]) && (S)[1] == ':')
-
-#ifdef GIT_WIN32
-static bool looks_like_network_computer_name(const char *path, int pos)
-{
- if (pos < 3)
- return false;
-
- if (path[0] != '/' || path[1] != '/')
- return false;
-
- while (pos-- > 2) {
- if (path[pos] == '/')
- return false;
- }
-
- return true;
-}
-#endif
-
-/*
- * Based on the Android implementation, BSD licensed.
- * http://android.git.kernel.org/
- *
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-int git_path_basename_r(git_buf *buffer, const char *path)
-{
- const char *endp, *startp;
- int len, result;
-
- /* Empty or NULL string gets treated as "." */
- if (path == NULL || *path == '\0') {
- startp = ".";
- len = 1;
- goto Exit;
- }
-
- /* Strip trailing slashes */
- endp = path + strlen(path) - 1;
- while (endp > path && *endp == '/')
- endp--;
-
- /* All slashes becomes "/" */
- if (endp == path && *endp == '/') {
- startp = "/";
- len = 1;
- goto Exit;
- }
-
- /* Find the start of the base */
- startp = endp;
- while (startp > path && *(startp - 1) != '/')
- startp--;
-
- /* Cast is safe because max path < max int */
- len = (int)(endp - startp + 1);
-
-Exit:
- result = len;
-
- if (buffer != NULL && git_buf_set(buffer, startp, len) < 0)
- return -1;
-
- return result;
-}
-
-/*
- * Based on the Android implementation, BSD licensed.
- * Check http://android.git.kernel.org/
- */
-int git_path_dirname_r(git_buf *buffer, const char *path)
-{
- const char *endp;
- int result, len;
-
- /* Empty or NULL string gets treated as "." */
- if (path == NULL || *path == '\0') {
- path = ".";
- len = 1;
- goto Exit;
- }
-
- /* Strip trailing slashes */
- endp = path + strlen(path) - 1;
- while (endp > path && *endp == '/')
- endp--;
-
- /* Find the start of the dir */
- while (endp > path && *endp != '/')
- endp--;
-
- /* Either the dir is "/" or there are no slashes */
- if (endp == path) {
- path = (*endp == '/') ? "/" : ".";
- len = 1;
- goto Exit;
- }
-
- do {
- endp--;
- } while (endp > path && *endp == '/');
-
- /* Cast is safe because max path < max int */
- len = (int)(endp - path + 1);
-
-#ifdef GIT_WIN32
- /* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
- 'C:/' here */
-
- if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) {
- len = 3;
- goto Exit;
- }
-
- /* Similarly checks if we're dealing with a network computer name
- '//computername/.git' will return '//computername/' */
-
- if (looks_like_network_computer_name(path, len)) {
- len++;
- goto Exit;
- }
-
-#endif
-
-Exit:
- result = len;
-
- if (buffer != NULL && git_buf_set(buffer, path, len) < 0)
- return -1;
-
- return result;
-}
-
-
-char *git_path_dirname(const char *path)
-{
- git_buf buf = GIT_BUF_INIT;
- char *dirname;
-
- git_path_dirname_r(&buf, path);
- dirname = git_buf_detach(&buf);
- git_buf_free(&buf); /* avoid memleak if error occurs */
-
- return dirname;
-}
-
-char *git_path_basename(const char *path)
-{
- git_buf buf = GIT_BUF_INIT;
- char *basename;
-
- git_path_basename_r(&buf, path);
- basename = git_buf_detach(&buf);
- git_buf_free(&buf); /* avoid memleak if error occurs */
-
- return basename;
-}
-
-size_t git_path_basename_offset(git_buf *buffer)
-{
- ssize_t slash;
-
- if (!buffer || buffer->size <= 0)
- return 0;
-
- slash = git_buf_rfind_next(buffer, '/');
-
- if (slash >= 0 && buffer->ptr[slash] == '/')
- return (size_t)(slash + 1);
-
- return 0;
-}
-
-const char *git_path_topdir(const char *path)
-{
- size_t len;
- ssize_t i;
-
- assert(path);
- len = strlen(path);
-
- if (!len || path[len - 1] != '/')
- return NULL;
-
- for (i = (ssize_t)len - 2; i >= 0; --i)
- if (path[i] == '/')
- break;
-
- return &path[i + 1];
-}
-
-int git_path_root(const char *path)
-{
- int offset = 0;
-
- /* Does the root of the path look like a windows drive ? */
- if (LOOKS_LIKE_DRIVE_PREFIX(path))
- offset += 2;
-
-#ifdef GIT_WIN32
- /* Are we dealing with a windows network path? */
- else if ((path[0] == '/' && path[1] == '/' && path[2] != '/') ||
- (path[0] == '\\' && path[1] == '\\' && path[2] != '\\'))
- {
- offset += 2;
-
- /* Skip the computer name segment */
- while (path[offset] && path[offset] != '/' && path[offset] != '\\')
- offset++;
- }
-#endif
-
- if (path[offset] == '/' || path[offset] == '\\')
- return offset;
-
- return -1; /* Not a real error - signals that path is not rooted */
-}
-
-void git_path_trim_slashes(git_buf *path)
-{
- int ceiling = git_path_root(path->ptr) + 1;
- assert(ceiling >= 0);
-
- while (path->size > (size_t)ceiling) {
- if (path->ptr[path->size-1] != '/')
- break;
-
- path->ptr[path->size-1] = '\0';
- path->size--;
- }
-}
-
-int git_path_join_unrooted(
- git_buf *path_out, const char *path, const char *base, ssize_t *root_at)
-{
- ssize_t root;
-
- assert(path && path_out);
-
- root = (ssize_t)git_path_root(path);
-
- if (base != NULL && root < 0) {
- if (git_buf_joinpath(path_out, base, path) < 0)
- return -1;
-
- root = (ssize_t)strlen(base);
- } else {
- if (git_buf_sets(path_out, path) < 0)
- return -1;
-
- if (root < 0)
- root = 0;
- else if (base)
- git_path_equal_or_prefixed(base, path, &root);
- }
-
- if (root_at)
- *root_at = root;
-
- return 0;
-}
-
-void git_path_squash_slashes(git_buf *path)
-{
- char *p, *q;
-
- if (path->size == 0)
- return;
-
- for (p = path->ptr, q = path->ptr; *q; p++, q++) {
- *p = *q;
-
- while (*q == '/' && *(q+1) == '/') {
- path->size--;
- q++;
- }
- }
-
- *p = '\0';
-}
-
-int git_path_prettify(git_buf *path_out, const char *path, const char *base)
-{
- char buf[GIT_PATH_MAX];
-
- assert(path && path_out);
-
- /* construct path if needed */
- if (base != NULL && git_path_root(path) < 0) {
- if (git_buf_joinpath(path_out, base, path) < 0)
- return -1;
- path = path_out->ptr;
- }
-
- if (p_realpath(path, buf) == NULL) {
- /* giterr_set resets the errno when dealing with a GITERR_OS kind of error */
- int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1;
- giterr_set(GITERR_OS, "Failed to resolve path '%s'", path);
-
- git_buf_clear(path_out);
-
- return error;
- }
-
- return git_buf_sets(path_out, buf);
-}
-
-int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base)
-{
- int error = git_path_prettify(path_out, path, base);
- return (error < 0) ? error : git_path_to_dir(path_out);
-}
-
-int git_path_to_dir(git_buf *path)
-{
- if (path->asize > 0 &&
- git_buf_len(path) > 0 &&
- path->ptr[git_buf_len(path) - 1] != '/')
- git_buf_putc(path, '/');
-
- return git_buf_oom(path) ? -1 : 0;
-}
-
-void git_path_string_to_dir(char* path, size_t size)
-{
- size_t end = strlen(path);
-
- if (end && path[end - 1] != '/' && end < size) {
- path[end] = '/';
- path[end + 1] = '\0';
- }
-}
-
-int git__percent_decode(git_buf *decoded_out, const char *input)
-{
- int len, hi, lo, i;
- assert(decoded_out && input);
-
- len = (int)strlen(input);
- git_buf_clear(decoded_out);
-
- for(i = 0; i < len; i++)
- {
- char c = input[i];
-
- if (c != '%')
- goto append;
-
- if (i >= len - 2)
- goto append;
-
- hi = git__fromhex(input[i + 1]);
- lo = git__fromhex(input[i + 2]);
-
- if (hi < 0 || lo < 0)
- goto append;
-
- c = (char)(hi << 4 | lo);
- i += 2;
-
-append:
- if (git_buf_putc(decoded_out, c) < 0)
- return -1;
- }
-
- return 0;
-}
-
-static int error_invalid_local_file_uri(const char *uri)
-{
- giterr_set(GITERR_CONFIG, "'%s' is not a valid local file URI", uri);
- return -1;
-}
-
-static int local_file_url_prefixlen(const char *file_url)
-{
- int len = -1;
-
- if (git__prefixcmp(file_url, "file://") == 0) {
- if (file_url[7] == '/')
- len = 8;
- else if (git__prefixcmp(file_url + 7, "localhost/") == 0)
- len = 17;
- }
-
- return len;
-}
-
-bool git_path_is_local_file_url(const char *file_url)
-{
- return (local_file_url_prefixlen(file_url) > 0);
-}
-
-int git_path_fromurl(git_buf *local_path_out, const char *file_url)
-{
- int offset;
-
- assert(local_path_out && file_url);
-
- if ((offset = local_file_url_prefixlen(file_url)) < 0 ||
- file_url[offset] == '\0' || file_url[offset] == '/')
- return error_invalid_local_file_uri(file_url);
-
-#ifndef GIT_WIN32
- offset--; /* A *nix absolute path starts with a forward slash */
-#endif
-
- git_buf_clear(local_path_out);
- return git__percent_decode(local_path_out, file_url + offset);
-}
-
-int git_path_walk_up(
- git_buf *path,
- const char *ceiling,
- int (*cb)(void *data, const char *),
- void *data)
-{
- int error = 0;
- git_buf iter;
- ssize_t stop = 0, scan;
- char oldc = '\0';
-
- assert(path && cb);
-
- if (ceiling != NULL) {
- if (git__prefixcmp(path->ptr, ceiling) == 0)
- stop = (ssize_t)strlen(ceiling);
- else
- stop = git_buf_len(path);
- }
- scan = git_buf_len(path);
-
- /* empty path: yield only once */
- if (!scan) {
- error = cb(data, "");
- if (error)
- giterr_set_after_callback(error);
- return error;
- }
-
- iter.ptr = path->ptr;
- iter.size = git_buf_len(path);
- iter.asize = path->asize;
-
- while (scan >= stop) {
- error = cb(data, iter.ptr);
- iter.ptr[scan] = oldc;
-
- if (error) {
- giterr_set_after_callback(error);
- break;
- }
-
- scan = git_buf_rfind_next(&iter, '/');
- if (scan >= 0) {
- scan++;
- oldc = iter.ptr[scan];
- iter.size = scan;
- iter.ptr[scan] = '\0';
- }
- }
-
- if (scan >= 0)
- iter.ptr[scan] = oldc;
-
- /* relative path: yield for the last component */
- if (!error && stop == 0 && iter.ptr[0] != '/') {
- error = cb(data, "");
- if (error)
- giterr_set_after_callback(error);
- }
-
- return error;
-}
-
-bool git_path_exists(const char *path)
-{
- assert(path);
- return p_access(path, F_OK) == 0;
-}
-
-bool git_path_isdir(const char *path)
-{
- struct stat st;
- if (p_stat(path, &st) < 0)
- return false;
-
- return S_ISDIR(st.st_mode) != 0;
-}
-
-bool git_path_isfile(const char *path)
-{
- struct stat st;
-
- assert(path);
- if (p_stat(path, &st) < 0)
- return false;
-
- return S_ISREG(st.st_mode) != 0;
-}
-
-bool git_path_islink(const char *path)
-{
- struct stat st;
-
- assert(path);
- if (p_lstat(path, &st) < 0)
- return false;
-
- return S_ISLNK(st.st_mode) != 0;
-}
-
-#ifdef GIT_WIN32
-
-bool git_path_is_empty_dir(const char *path)
-{
- git_win32_path filter_w;
- bool empty = false;
-
- if (git_win32__findfirstfile_filter(filter_w, path)) {
- WIN32_FIND_DATAW findData;
- HANDLE hFind = FindFirstFileW(filter_w, &findData);
-
- /* FindFirstFile will fail if there are no children to the given
- * path, which can happen if the given path is a file (and obviously
- * has no children) or if the given path is an empty mount point.
- * (Most directories have at least directory entries '.' and '..',
- * but ridiculously another volume mounted in another drive letter's
- * path space do not, and thus have nothing to enumerate.) If
- * FindFirstFile fails, check if this is a directory-like thing
- * (a mount point).
- */
- if (hFind == INVALID_HANDLE_VALUE)
- return git_path_isdir(path);
-
- /* If the find handle was created successfully, then it's a directory */
- empty = true;
-
- do {
- /* Allow the enumeration to return . and .. and still be considered
- * empty. In the special case of drive roots (i.e. C:\) where . and
- * .. do not occur, we can still consider the path to be an empty
- * directory if there's nothing there. */
- if (!git_path_is_dot_or_dotdotW(findData.cFileName)) {
- empty = false;
- break;
- }
- } while (FindNextFileW(hFind, &findData));
-
- FindClose(hFind);
- }
-
- return empty;
-}
-
-#else
-
-static int path_found_entry(void *payload, git_buf *path)
-{
- GIT_UNUSED(payload);
- return !git_path_is_dot_or_dotdot(path->ptr);
-}
-
-bool git_path_is_empty_dir(const char *path)
-{
- int error;
- git_buf dir = GIT_BUF_INIT;
-
- if (!git_path_isdir(path))
- return false;
-
- if ((error = git_buf_sets(&dir, path)) != 0)
- giterr_clear();
- else
- error = git_path_direach(&dir, 0, path_found_entry, NULL);
-
- git_buf_free(&dir);
-
- return !error;
-}
-
-#endif
-
-int git_path_set_error(int errno_value, const char *path, const char *action)
-{
- switch (errno_value) {
- case ENOENT:
- case ENOTDIR:
- giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action);
- return GIT_ENOTFOUND;
-
- case EINVAL:
- case ENAMETOOLONG:
- giterr_set(GITERR_OS, "Invalid path for filesystem '%s'", path);
- return GIT_EINVALIDSPEC;
-
- case EEXIST:
- giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path);
- return GIT_EEXISTS;
-
- case EACCES:
- giterr_set(GITERR_OS, "Failed %s - '%s' is locked", action, path);
- return GIT_ELOCKED;
-
- default:
- giterr_set(GITERR_OS, "Could not %s '%s'", action, path);
- return -1;
- }
-}
-
-int git_path_lstat(const char *path, struct stat *st)
-{
- if (p_lstat(path, st) == 0)
- return 0;
-
- return git_path_set_error(errno, path, "stat");
-}
-
-static bool _check_dir_contents(
- git_buf *dir,
- const char *sub,
- bool (*predicate)(const char *))
-{
- bool result;
- size_t dir_size = git_buf_len(dir);
- size_t sub_size = strlen(sub);
- size_t alloc_size;
-
- /* leave base valid even if we could not make space for subdir */
- if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) ||
- GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) ||
- git_buf_try_grow(dir, alloc_size, false) < 0)
- return false;
-
- /* save excursion */
- git_buf_joinpath(dir, dir->ptr, sub);
-
- result = predicate(dir->ptr);
-
- /* restore path */
- git_buf_truncate(dir, dir_size);
- return result;
-}
-
-bool git_path_contains(git_buf *dir, const char *item)
-{
- return _check_dir_contents(dir, item, &git_path_exists);
-}
-
-bool git_path_contains_dir(git_buf *base, const char *subdir)
-{
- return _check_dir_contents(base, subdir, &git_path_isdir);
-}
-
-bool git_path_contains_file(git_buf *base, const char *file)
-{
- return _check_dir_contents(base, file, &git_path_isfile);
-}
-
-int git_path_find_dir(git_buf *dir, const char *path, const char *base)
-{
- int error = git_path_join_unrooted(dir, path, base, NULL);
-
- if (!error) {
- char buf[GIT_PATH_MAX];
- if (p_realpath(dir->ptr, buf) != NULL)
- error = git_buf_sets(dir, buf);
- }
-
- /* call dirname if this is not a directory */
- if (!error) /* && git_path_isdir(dir->ptr) == false) */
- error = (git_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0;
-
- if (!error)
- error = git_path_to_dir(dir);
-
- return error;
-}
-
-int git_path_resolve_relative(git_buf *path, size_t ceiling)
-{
- char *base, *to, *from, *next;
- size_t len;
-
- GITERR_CHECK_ALLOC_BUF(path);
-
- if (ceiling > path->size)
- ceiling = path->size;
-
- /* recognize drive prefixes, etc. that should not be backed over */
- if (ceiling == 0)
- ceiling = git_path_root(path->ptr) + 1;
-
- /* recognize URL prefixes that should not be backed over */
- if (ceiling == 0) {
- for (next = path->ptr; *next && git__isalpha(*next); ++next);
- if (next[0] == ':' && next[1] == '/' && next[2] == '/')
- ceiling = (next + 3) - path->ptr;
- }
-
- base = to = from = path->ptr + ceiling;
-
- while (*from) {
- for (next = from; *next && *next != '/'; ++next);
-
- len = next - from;
-
- if (len == 1 && from[0] == '.')
- /* do nothing with singleton dot */;
-
- else if (len == 2 && from[0] == '.' && from[1] == '.') {
- /* error out if trying to up one from a hard base */
- if (to == base && ceiling != 0) {
- giterr_set(GITERR_INVALID,
- "Cannot strip root component off url");
- return -1;
- }
-
- /* no more path segments to strip,
- * use '../' as a new base path */
- if (to == base) {
- if (*next == '/')
- len++;
-
- if (to != from)
- memmove(to, from, len);
-
- to += len;
- /* this is now the base, can't back up from a
- * relative prefix */
- base = to;
- } else {
- /* back up a path segment */
- while (to > base && to[-1] == '/') to--;
- while (to > base && to[-1] != '/') to--;
- }
- } else {
- if (*next == '/' && *from != '/')
- len++;
-
- if (to != from)
- memmove(to, from, len);
-
- to += len;
- }
-
- from += len;
-
- while (*from == '/') from++;
- }
-
- *to = '\0';
-
- path->size = to - path->ptr;
-
- return 0;
-}
-
-int git_path_apply_relative(git_buf *target, const char *relpath)
-{
- git_buf_joinpath(target, git_buf_cstr(target), relpath);
- return git_path_resolve_relative(target, 0);
-}
-
-int git_path_cmp(
- const char *name1, size_t len1, int isdir1,
- const char *name2, size_t len2, int isdir2,
- int (*compare)(const char *, const char *, size_t))
-{
- unsigned char c1, c2;
- size_t len = len1 < len2 ? len1 : len2;
- int cmp;
-
- cmp = compare(name1, name2, len);
- if (cmp)
- return cmp;
-
- c1 = name1[len];
- c2 = name2[len];
-
- if (c1 == '\0' && isdir1)
- c1 = '/';
-
- if (c2 == '\0' && isdir2)
- c2 = '/';
-
- return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
-}
-
-size_t git_path_common_dirlen(const char *one, const char *two)
-{
- const char *p, *q, *dirsep = NULL;
-
- for (p = one, q = two; *p && *q; p++, q++) {
- if (*p == '/' && *q == '/')
- dirsep = p;
- else if (*p != *q)
- break;
- }
-
- return dirsep ? (dirsep - one) + 1 : 0;
-}
-
-int git_path_make_relative(git_buf *path, const char *parent)
-{
- const char *p, *q, *p_dirsep, *q_dirsep;
- size_t plen = path->size, newlen, alloclen, depth = 1, i, offset;
-
- for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) {
- if (*p == '/' && *q == '/') {
- p_dirsep = p;
- q_dirsep = q;
- }
- else if (*p != *q)
- break;
- }
-
- /* need at least 1 common path segment */
- if ((p_dirsep == path->ptr || q_dirsep == parent) &&
- (*p_dirsep != '/' || *q_dirsep != '/')) {
- giterr_set(GITERR_INVALID,
- "%s is not a parent of %s", parent, path->ptr);
- return GIT_ENOTFOUND;
- }
-
- if (*p == '/' && !*q)
- p++;
- else if (!*p && *q == '/')
- q++;
- else if (!*p && !*q)
- return git_buf_clear(path), 0;
- else {
- p = p_dirsep + 1;
- q = q_dirsep + 1;
- }
-
- plen -= (p - path->ptr);
-
- if (!*q)
- return git_buf_set(path, p, plen);
-
- for (; (q = strchr(q, '/')) && *(q + 1); q++)
- depth++;
-
- GITERR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3);
- GITERR_CHECK_ALLOC_ADD(&newlen, newlen, plen);
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, newlen, 1);
-
- /* save the offset as we might realllocate the pointer */
- offset = p - path->ptr;
- if (git_buf_try_grow(path, alloclen, 1) < 0)
- return -1;
- p = path->ptr + offset;
-
- memmove(path->ptr + (depth * 3), p, plen + 1);
-
- for (i = 0; i < depth; i++)
- memcpy(path->ptr + (i * 3), "../", 3);
-
- path->size = newlen;
- return 0;
-}
-
-bool git_path_has_non_ascii(const char *path, size_t pathlen)
-{
- const uint8_t *scan = (const uint8_t *)path, *end;
-
- for (end = scan + pathlen; scan < end; ++scan)
- if (*scan & 0x80)
- return true;
-
- return false;
-}
-
-#ifdef GIT_USE_ICONV
-
-int git_path_iconv_init_precompose(git_path_iconv_t *ic)
-{
- git_buf_init(&ic->buf, 0);
- ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING);
- return 0;
-}
-
-void git_path_iconv_clear(git_path_iconv_t *ic)
-{
- if (ic) {
- if (ic->map != (iconv_t)-1)
- iconv_close(ic->map);
- git_buf_free(&ic->buf);
- }
-}
-
-int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen)
-{
- char *nfd = (char*)*in, *nfc;
- size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv;
- int retry = 1;
-
- if (!ic || ic->map == (iconv_t)-1 ||
- !git_path_has_non_ascii(*in, *inlen))
- return 0;
-
- git_buf_clear(&ic->buf);
-
- while (1) {
- GITERR_CHECK_ALLOC_ADD(&alloclen, wantlen, 1);
- if (git_buf_grow(&ic->buf, alloclen) < 0)
- return -1;
-
- nfc = ic->buf.ptr + ic->buf.size;
- nfclen = ic->buf.asize - ic->buf.size;
-
- rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen);
-
- ic->buf.size = (nfc - ic->buf.ptr);
-
- if (rv != (size_t)-1)
- break;
-
- /* if we cannot convert the data (probably because iconv thinks
- * it is not valid UTF-8 source data), then use original data
- */
- if (errno != E2BIG)
- return 0;
-
- /* make space for 2x the remaining data to be converted
- * (with per retry overhead to avoid infinite loops)
- */
- wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4);
-
- if (retry++ > 4)
- goto fail;
- }
-
- ic->buf.ptr[ic->buf.size] = '\0';
-
- *in = ic->buf.ptr;
- *inlen = ic->buf.size;
-
- return 0;
-
-fail:
- giterr_set(GITERR_OS, "Unable to convert unicode path data");
- return -1;
-}
-
-static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
-static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
-
-/* Check if the platform is decomposing unicode data for us. We will
- * emulate core Git and prefer to use precomposed unicode data internally
- * on these platforms, composing the decomposed unicode on the fly.
- *
- * This mainly happens on the Mac where HDFS stores filenames as
- * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
- * return decomposed unicode from readdir() even when the actual
- * filesystem is storing precomposed unicode.
- */
-bool git_path_does_fs_decompose_unicode(const char *root)
-{
- git_buf path = GIT_BUF_INIT;
- int fd;
- bool found_decomposed = false;
- char tmp[6];
-
- /* Create a file using a precomposed path and then try to find it
- * using the decomposed name. If the lookup fails, then we will mark
- * that we should precompose unicode for this repository.
- */
- if (git_buf_joinpath(&path, root, nfc_file) < 0 ||
- (fd = p_mkstemp(path.ptr)) < 0)
- goto done;
- p_close(fd);
-
- /* record trailing digits generated by mkstemp */
- memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp));
-
- /* try to look up as NFD path */
- if (git_buf_joinpath(&path, root, nfd_file) < 0)
- goto done;
- memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
-
- found_decomposed = git_path_exists(path.ptr);
-
- /* remove temporary file (using original precomposed path) */
- if (git_buf_joinpath(&path, root, nfc_file) < 0)
- goto done;
- memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
-
- (void)p_unlink(path.ptr);
-
-done:
- git_buf_free(&path);
- return found_decomposed;
-}
-
-#else
-
-bool git_path_does_fs_decompose_unicode(const char *root)
-{
- GIT_UNUSED(root);
- return false;
-}
-
-#endif
-
-#if defined(__sun) || defined(__GNU__)
-typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1];
-#else
-typedef struct dirent path_dirent_data;
-#endif
-
-int git_path_direach(
- git_buf *path,
- uint32_t flags,
- int (*fn)(void *, git_buf *),
- void *arg)
-{
- int error = 0;
- ssize_t wd_len;
- DIR *dir;
- struct dirent *de;
-
-#ifdef GIT_USE_ICONV
- git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
-#endif
-
- GIT_UNUSED(flags);
-
- if (git_path_to_dir(path) < 0)
- return -1;
-
- wd_len = git_buf_len(path);
-
- if ((dir = opendir(path->ptr)) == NULL) {
- giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr);
- if (errno == ENOENT)
- return GIT_ENOTFOUND;
-
- return -1;
- }
-
-#ifdef GIT_USE_ICONV
- if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
- (void)git_path_iconv_init_precompose(&ic);
-#endif
-
- while ((de = readdir(dir)) != NULL) {
- const char *de_path = de->d_name;
- size_t de_len = strlen(de_path);
-
- if (git_path_is_dot_or_dotdot(de_path))
- continue;
-
-#ifdef GIT_USE_ICONV
- if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0)
- break;
-#endif
-
- if ((error = git_buf_put(path, de_path, de_len)) < 0)
- break;
-
- giterr_clear();
- error = fn(arg, path);
-
- git_buf_truncate(path, wd_len); /* restore path */
-
- /* Only set our own error if the callback did not set one already */
- if (error != 0) {
- if (!giterr_last())
- giterr_set_after_callback(error);
-
- break;
- }
- }
-
- closedir(dir);
-
-#ifdef GIT_USE_ICONV
- git_path_iconv_clear(&ic);
-#endif
-
- return error;
-}
-
-#if defined(GIT_WIN32) && !defined(__MINGW32__)
-
-/* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
- * and better.
- */
-#ifndef FIND_FIRST_EX_LARGE_FETCH
-# define FIND_FIRST_EX_LARGE_FETCH 2
-#endif
-
-int git_path_diriter_init(
- git_path_diriter *diriter,
- const char *path,
- unsigned int flags)
-{
- git_win32_path path_filter;
-
- static int is_win7_or_later = -1;
- if (is_win7_or_later < 0)
- is_win7_or_later = git_has_win32_version(6, 1, 0);
-
- assert(diriter && path);
-
- memset(diriter, 0, sizeof(git_path_diriter));
- diriter->handle = INVALID_HANDLE_VALUE;
-
- if (git_buf_puts(&diriter->path_utf8, path) < 0)
- return -1;
-
- git_path_trim_slashes(&diriter->path_utf8);
-
- if (diriter->path_utf8.size == 0) {
- giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path);
- return -1;
- }
-
- if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 ||
- !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) {
- giterr_set(GITERR_OS, "Could not parse the directory path '%s'", path);
- return -1;
- }
-
- diriter->handle = FindFirstFileExW(
- path_filter,
- is_win7_or_later ? FindExInfoBasic : FindExInfoStandard,
- &diriter->current,
- FindExSearchNameMatch,
- NULL,
- is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0);
-
- if (diriter->handle == INVALID_HANDLE_VALUE) {
- giterr_set(GITERR_OS, "Could not open directory '%s'", path);
- return -1;
- }
-
- diriter->parent_utf8_len = diriter->path_utf8.size;
- diriter->flags = flags;
- return 0;
-}
-
-static int diriter_update_paths(git_path_diriter *diriter)
-{
- size_t filename_len, path_len;
-
- filename_len = wcslen(diriter->current.cFileName);
-
- if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) ||
- GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2))
- return -1;
-
- if (path_len > GIT_WIN_PATH_UTF16) {
- giterr_set(GITERR_FILESYSTEM,
- "invalid path '%.*ls\\%ls' (path too long)",
- diriter->parent_len, diriter->path, diriter->current.cFileName);
- return -1;
- }
-
- diriter->path[diriter->parent_len] = L'\\';
- memcpy(&diriter->path[diriter->parent_len+1],
- diriter->current.cFileName, filename_len * sizeof(wchar_t));
- diriter->path[path_len-1] = L'\0';
-
- git_buf_truncate(&diriter->path_utf8, diriter->parent_utf8_len);
-
- if (diriter->parent_utf8_len > 0 &&
- diriter->path_utf8.ptr[diriter->parent_utf8_len-1] != '/')
- git_buf_putc(&diriter->path_utf8, '/');
-
- git_buf_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len);
-
- if (git_buf_oom(&diriter->path_utf8))
- return -1;
-
- return 0;
-}
-
-int git_path_diriter_next(git_path_diriter *diriter)
-{
- bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
-
- do {
- /* Our first time through, we already have the data from
- * FindFirstFileW. Use it, otherwise get the next file.
- */
- if (!diriter->needs_next)
- diriter->needs_next = 1;
- else if (!FindNextFileW(diriter->handle, &diriter->current))
- return GIT_ITEROVER;
- } while (skip_dot && git_path_is_dot_or_dotdotW(diriter->current.cFileName));
-
- if (diriter_update_paths(diriter) < 0)
- return -1;
-
- return 0;
-}
-
-int git_path_diriter_filename(
- const char **out,
- size_t *out_len,
- git_path_diriter *diriter)
-{
- assert(out && out_len && diriter);
-
- assert(diriter->path_utf8.size > diriter->parent_utf8_len);
-
- *out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1];
- *out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1;
- return 0;
-}
-
-int git_path_diriter_fullpath(
- const char **out,
- size_t *out_len,
- git_path_diriter *diriter)
-{
- assert(out && out_len && diriter);
-
- *out = diriter->path_utf8.ptr;
- *out_len = diriter->path_utf8.size;
- return 0;
-}
-
-int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter)
-{
- assert(out && diriter);
-
- return git_win32__file_attribute_to_stat(out,
- (WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current,
- diriter->path);
-}
-
-void git_path_diriter_free(git_path_diriter *diriter)
-{
- if (diriter == NULL)
- return;
-
- git_buf_free(&diriter->path_utf8);
-
- if (diriter->handle != INVALID_HANDLE_VALUE) {
- FindClose(diriter->handle);
- diriter->handle = INVALID_HANDLE_VALUE;
- }
-}
-
-#else
-
-int git_path_diriter_init(
- git_path_diriter *diriter,
- const char *path,
- unsigned int flags)
-{
- assert(diriter && path);
-
- memset(diriter, 0, sizeof(git_path_diriter));
-
- if (git_buf_puts(&diriter->path, path) < 0)
- return -1;
-
- git_path_trim_slashes(&diriter->path);
-
- if (diriter->path.size == 0) {
- giterr_set(GITERR_FILESYSTEM, "Could not open directory '%s'", path);
- return -1;
- }
-
- if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) {
- git_buf_free(&diriter->path);
-
- giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
- return -1;
- }
-
-#ifdef GIT_USE_ICONV
- if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
- (void)git_path_iconv_init_precompose(&diriter->ic);
-#endif
-
- diriter->parent_len = diriter->path.size;
- diriter->flags = flags;
-
- return 0;
-}
-
-int git_path_diriter_next(git_path_diriter *diriter)
-{
- struct dirent *de;
- const char *filename;
- size_t filename_len;
- bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
- int error = 0;
-
- assert(diriter);
-
- errno = 0;
-
- do {
- if ((de = readdir(diriter->dir)) == NULL) {
- if (!errno)
- return GIT_ITEROVER;
-
- giterr_set(GITERR_OS,
- "Could not read directory '%s'", diriter->path.ptr);
- return -1;
- }
- } while (skip_dot && git_path_is_dot_or_dotdot(de->d_name));
-
- filename = de->d_name;
- filename_len = strlen(filename);
-
-#ifdef GIT_USE_ICONV
- if ((diriter->flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0 &&
- (error = git_path_iconv(&diriter->ic, &filename, &filename_len)) < 0)
- return error;
-#endif
-
- git_buf_truncate(&diriter->path, diriter->parent_len);
-
- if (diriter->parent_len > 0 &&
- diriter->path.ptr[diriter->parent_len-1] != '/')
- git_buf_putc(&diriter->path, '/');
-
- git_buf_put(&diriter->path, filename, filename_len);
-
- if (git_buf_oom(&diriter->path))
- return -1;
-
- return error;
-}
-
-int git_path_diriter_filename(
- const char **out,
- size_t *out_len,
- git_path_diriter *diriter)
-{
- assert(out && out_len && diriter);
-
- assert(diriter->path.size > diriter->parent_len);
-
- *out = &diriter->path.ptr[diriter->parent_len+1];
- *out_len = diriter->path.size - diriter->parent_len - 1;
- return 0;
-}
-
-int git_path_diriter_fullpath(
- const char **out,
- size_t *out_len,
- git_path_diriter *diriter)
-{
- assert(out && out_len && diriter);
-
- *out = diriter->path.ptr;
- *out_len = diriter->path.size;
- return 0;
-}
-
-int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter)
-{
- assert(out && diriter);
-
- return git_path_lstat(diriter->path.ptr, out);
-}
-
-void git_path_diriter_free(git_path_diriter *diriter)
-{
- if (diriter == NULL)
- return;
-
- if (diriter->dir) {
- closedir(diriter->dir);
- diriter->dir = NULL;
- }
-
-#ifdef GIT_USE_ICONV
- git_path_iconv_clear(&diriter->ic);
-#endif
-
- git_buf_free(&diriter->path);
-}
-
-#endif
-
-int git_path_dirload(
- git_vector *contents,
- const char *path,
- size_t prefix_len,
- uint32_t flags)
-{
- git_path_diriter iter = GIT_PATH_DIRITER_INIT;
- const char *name;
- size_t name_len;
- char *dup;
- int error;
-
- assert(contents && path);
-
- if ((error = git_path_diriter_init(&iter, path, flags)) < 0)
- return error;
-
- while ((error = git_path_diriter_next(&iter)) == 0) {
- if ((error = git_path_diriter_fullpath(&name, &name_len, &iter)) < 0)
- break;
-
- assert(name_len > prefix_len);
-
- dup = git__strndup(name + prefix_len, name_len - prefix_len);
- GITERR_CHECK_ALLOC(dup);
-
- if ((error = git_vector_insert(contents, dup)) < 0)
- break;
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- git_path_diriter_free(&iter);
- return error;
-}
-
-int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
-{
- if (git_path_is_local_file_url(url_or_path))
- return git_path_fromurl(local_path_out, url_or_path);
- else
- return git_buf_sets(local_path_out, url_or_path);
-}
-
-/* Reject paths like AUX or COM1, or those versions that end in a dot or
- * colon. ("AUX." or "AUX:")
- */
-GIT_INLINE(bool) verify_dospath(
- const char *component,
- size_t len,
- const char dospath[3],
- bool trailing_num)
-{
- size_t last = trailing_num ? 4 : 3;
-
- if (len < last || git__strncasecmp(component, dospath, 3) != 0)
- return true;
-
- if (trailing_num && (component[3] < '1' || component[3] > '9'))
- return true;
-
- return (len > last &&
- component[last] != '.' &&
- component[last] != ':');
-}
-
-static int32_t next_hfs_char(const char **in, size_t *len)
-{
- while (*len) {
- int32_t codepoint;
- int cp_len = git__utf8_iterate((const uint8_t *)(*in), (int)(*len), &codepoint);
- if (cp_len < 0)
- return -1;
-
- (*in) += cp_len;
- (*len) -= cp_len;
-
- /* these code points are ignored completely */
- switch (codepoint) {
- case 0x200c: /* ZERO WIDTH NON-JOINER */
- case 0x200d: /* ZERO WIDTH JOINER */
- case 0x200e: /* LEFT-TO-RIGHT MARK */
- case 0x200f: /* RIGHT-TO-LEFT MARK */
- case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */
- case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */
- case 0x202c: /* POP DIRECTIONAL FORMATTING */
- case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */
- case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */
- case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */
- case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */
- case 0x206c: /* INHIBIT ARABIC FORM SHAPING */
- case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */
- case 0x206e: /* NATIONAL DIGIT SHAPES */
- case 0x206f: /* NOMINAL DIGIT SHAPES */
- case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */
- continue;
- }
-
- /* fold into lowercase -- this will only fold characters in
- * the ASCII range, which is perfectly fine, because the
- * git folder name can only be composed of ascii characters
- */
- return git__tolower(codepoint);
- }
- return 0; /* NULL byte -- end of string */
-}
-
-static bool verify_dotgit_hfs(const char *path, size_t len)
-{
- if (next_hfs_char(&path, &len) != '.' ||
- next_hfs_char(&path, &len) != 'g' ||
- next_hfs_char(&path, &len) != 'i' ||
- next_hfs_char(&path, &len) != 't' ||
- next_hfs_char(&path, &len) != 0)
- return true;
-
- return false;
-}
-
-GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len)
-{
- git_buf *reserved = git_repository__reserved_names_win32;
- size_t reserved_len = git_repository__reserved_names_win32_len;
- size_t start = 0, i;
-
- if (repo)
- git_repository__reserved_names(&reserved, &reserved_len, repo, true);
-
- for (i = 0; i < reserved_len; i++) {
- git_buf *r = &reserved[i];
-
- if (len >= r->size &&
- strncasecmp(path, r->ptr, r->size) == 0) {
- start = r->size;
- break;
- }
- }
-
- if (!start)
- return true;
-
- /* Reject paths like ".git\" */
- if (path[start] == '\\')
- return false;
-
- /* Reject paths like '.git ' or '.git.' */
- for (i = start; i < len; i++) {
- if (path[i] != ' ' && path[i] != '.')
- return true;
- }
-
- return false;
-}
-
-GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags)
-{
- if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\')
- return false;
-
- if ((flags & GIT_PATH_REJECT_SLASH) && c == '/')
- return false;
-
- if (flags & GIT_PATH_REJECT_NT_CHARS) {
- if (c < 32)
- return false;
-
- switch (c) {
- case '<':
- case '>':
- case ':':
- case '"':
- case '|':
- case '?':
- case '*':
- return false;
- }
- }
-
- return true;
-}
-
-/*
- * We fundamentally don't like some paths when dealing with user-inputted
- * strings (in checkout or ref names): we don't want dot or dot-dot
- * anywhere, we want to avoid writing weird paths on Windows that can't
- * be handled by tools that use the non-\\?\ APIs, we don't want slashes
- * or double slashes at the end of paths that can make them ambiguous.
- *
- * For checkout, we don't want to recurse into ".git" either.
- */
-static bool verify_component(
- git_repository *repo,
- const char *component,
- size_t len,
- unsigned int flags)
-{
- if (len == 0)
- return false;
-
- if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
- len == 1 && component[0] == '.')
- return false;
-
- if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
- len == 2 && component[0] == '.' && component[1] == '.')
- return false;
-
- if ((flags & GIT_PATH_REJECT_TRAILING_DOT) && component[len-1] == '.')
- return false;
-
- if ((flags & GIT_PATH_REJECT_TRAILING_SPACE) && component[len-1] == ' ')
- return false;
-
- if ((flags & GIT_PATH_REJECT_TRAILING_COLON) && component[len-1] == ':')
- return false;
-
- if (flags & GIT_PATH_REJECT_DOS_PATHS) {
- if (!verify_dospath(component, len, "CON", false) ||
- !verify_dospath(component, len, "PRN", false) ||
- !verify_dospath(component, len, "AUX", false) ||
- !verify_dospath(component, len, "NUL", false) ||
- !verify_dospath(component, len, "COM", true) ||
- !verify_dospath(component, len, "LPT", true))
- return false;
- }
-
- if (flags & GIT_PATH_REJECT_DOT_GIT_HFS &&
- !verify_dotgit_hfs(component, len))
- return false;
-
- if (flags & GIT_PATH_REJECT_DOT_GIT_NTFS &&
- !verify_dotgit_ntfs(repo, component, len))
- return false;
-
- /* don't bother rerunning the `.git` test if we ran the HFS or NTFS
- * specific tests, they would have already rejected `.git`.
- */
- if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 &&
- (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 &&
- (flags & GIT_PATH_REJECT_DOT_GIT_LITERAL) &&
- len == 4 &&
- component[0] == '.' &&
- (component[1] == 'g' || component[1] == 'G') &&
- (component[2] == 'i' || component[2] == 'I') &&
- (component[3] == 't' || component[3] == 'T'))
- return false;
-
- return true;
-}
-
-GIT_INLINE(unsigned int) dotgit_flags(
- git_repository *repo,
- unsigned int flags)
-{
- int protectHFS = 0, protectNTFS = 0;
-
- flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL;
-
-#ifdef __APPLE__
- protectHFS = 1;
-#endif
-
-#ifdef GIT_WIN32
- protectNTFS = 1;
-#endif
-
- if (repo && !protectHFS)
- git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS);
- if (protectHFS)
- flags |= GIT_PATH_REJECT_DOT_GIT_HFS;
-
- if (repo && !protectNTFS)
- git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS);
- if (protectNTFS)
- flags |= GIT_PATH_REJECT_DOT_GIT_NTFS;
-
- return flags;
-}
-
-bool git_path_isvalid(
- git_repository *repo,
- const char *path,
- unsigned int flags)
-{
- const char *start, *c;
-
- /* Upgrade the ".git" checks based on platform */
- if ((flags & GIT_PATH_REJECT_DOT_GIT))
- flags = dotgit_flags(repo, flags);
-
- for (start = c = path; *c; c++) {
- if (!verify_char(*c, flags))
- return false;
-
- if (*c == '/') {
- if (!verify_component(repo, start, (c - start), flags))
- return false;
-
- start = c+1;
- }
- }
-
- return verify_component(repo, start, (c - start), flags);
-}
-
-int git_path_normalize_slashes(git_buf *out, const char *path)
-{
- int error;
- char *p;
-
- if ((error = git_buf_puts(out, path)) < 0)
- return error;
-
- for (p = out->ptr; *p; p++) {
- if (*p == '\\')
- *p = '/';
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_path_h__
-#define INCLUDE_path_h__
-
-#include "common.h"
-#include "posix.h"
-#include "buffer.h"
-#include "vector.h"
-
-/**
- * Path manipulation utils
- *
- * These are path utilities that munge paths without actually
- * looking at the real filesystem.
- */
-
-/*
- * The dirname() function shall take a pointer to a character string
- * that contains a pathname, and return a pointer to a string that is a
- * pathname of the parent directory of that file. Trailing '/' characters
- * in the path are not counted as part of the path.
- *
- * If path does not contain a '/', then dirname() shall return a pointer to
- * the string ".". If path is a null pointer or points to an empty string,
- * dirname() shall return a pointer to the string "." .
- *
- * The `git_path_dirname` implementation is thread safe. The returned
- * string must be manually free'd.
- *
- * The `git_path_dirname_r` implementation writes the dirname to a `git_buf`
- * if the buffer pointer is not NULL.
- * It returns an error code < 0 if there is an allocation error, otherwise
- * the length of the dirname (which will be > 0).
- */
-extern char *git_path_dirname(const char *path);
-extern int git_path_dirname_r(git_buf *buffer, const char *path);
-
-/*
- * This function returns the basename of the file, which is the last
- * part of its full name given by fname, with the drive letter and
- * leading directories stripped off. For example, the basename of
- * c:/foo/bar/file.ext is file.ext, and the basename of a:foo is foo.
- *
- * Trailing slashes and backslashes are significant: the basename of
- * c:/foo/bar/ is an empty string after the rightmost slash.
- *
- * The `git_path_basename` implementation is thread safe. The returned
- * string must be manually free'd.
- *
- * The `git_path_basename_r` implementation writes the basename to a `git_buf`.
- * It returns an error code < 0 if there is an allocation error, otherwise
- * the length of the basename (which will be >= 0).
- */
-extern char *git_path_basename(const char *path);
-extern int git_path_basename_r(git_buf *buffer, const char *path);
-
-/* Return the offset of the start of the basename. Unlike the other
- * basename functions, this returns 0 if the path is empty.
- */
-extern size_t git_path_basename_offset(git_buf *buffer);
-
-extern const char *git_path_topdir(const char *path);
-
-/**
- * Find offset to root of path if path has one.
- *
- * This will return a number >= 0 which is the offset to the start of the
- * path, if the path is rooted (i.e. "/rooted/path" returns 0 and
- * "c:/windows/rooted/path" returns 2). If the path is not rooted, this
- * returns -1.
- */
-extern int git_path_root(const char *path);
-
-/**
- * Ensure path has a trailing '/'.
- */
-extern int git_path_to_dir(git_buf *path);
-
-/**
- * Ensure string has a trailing '/' if there is space for it.
- */
-extern void git_path_string_to_dir(char* path, size_t size);
-
-/**
- * Taken from git.git; returns nonzero if the given path is "." or "..".
- */
-GIT_INLINE(int) git_path_is_dot_or_dotdot(const char *name)
-{
- return (name[0] == '.' &&
- (name[1] == '\0' ||
- (name[1] == '.' && name[2] == '\0')));
-}
-
-#ifdef GIT_WIN32
-GIT_INLINE(int) git_path_is_dot_or_dotdotW(const wchar_t *name)
-{
- return (name[0] == L'.' &&
- (name[1] == L'\0' ||
- (name[1] == L'.' && name[2] == L'\0')));
-}
-
-/**
- * Convert backslashes in path to forward slashes.
- */
-GIT_INLINE(void) git_path_mkposix(char *path)
-{
- while (*path) {
- if (*path == '\\')
- *path = '/';
-
- path++;
- }
-}
-#else
-# define git_path_mkposix(p) /* blank */
-#endif
-
-/**
- * Check if string is a relative path (i.e. starts with "./" or "../")
- */
-GIT_INLINE(int) git_path_is_relative(const char *p)
-{
- return (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/')));
-}
-
-/**
- * Check if string is at end of path segment (i.e. looking at '/' or '\0')
- */
-GIT_INLINE(int) git_path_at_end_of_segment(const char *p)
-{
- return !*p || *p == '/';
-}
-
-extern int git__percent_decode(git_buf *decoded_out, const char *input);
-
-/**
- * Extract path from file:// URL.
- */
-extern int git_path_fromurl(git_buf *local_path_out, const char *file_url);
-
-
-/**
- * Path filesystem utils
- *
- * These are path utilities that actually access the filesystem.
- */
-
-/**
- * Check if a file exists and can be accessed.
- * @return true or false
- */
-extern bool git_path_exists(const char *path);
-
-/**
- * Check if the given path points to a directory.
- * @return true or false
- */
-extern bool git_path_isdir(const char *path);
-
-/**
- * Check if the given path points to a regular file.
- * @return true or false
- */
-extern bool git_path_isfile(const char *path);
-
-/**
- * Check if the given path points to a symbolic link.
- * @return true or false
- */
-extern bool git_path_islink(const char *path);
-
-/**
- * Check if the given path is a directory, and is empty.
- */
-extern bool git_path_is_empty_dir(const char *path);
-
-/**
- * Stat a file and/or link and set error if needed.
- */
-extern int git_path_lstat(const char *path, struct stat *st);
-
-/**
- * Check if the parent directory contains the item.
- *
- * @param dir Directory to check.
- * @param item Item that might be in the directory.
- * @return 0 if item exists in directory, <0 otherwise.
- */
-extern bool git_path_contains(git_buf *dir, const char *item);
-
-/**
- * Check if the given path contains the given subdirectory.
- *
- * @param parent Directory path that might contain subdir
- * @param subdir Subdirectory name to look for in parent
- * @return true if subdirectory exists, false otherwise.
- */
-extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
-
-/**
- * Determine the common directory length between two paths, including
- * the final path separator. For example, given paths 'a/b/c/1.txt
- * and 'a/b/c/d/2.txt', the common directory is 'a/b/c/', and this
- * will return the length of the string 'a/b/c/', which is 6.
- *
- * @param one The first path
- * @param two The second path
- * @return The length of the common directory
- */
-extern size_t git_path_common_dirlen(const char *one, const char *two);
-
-/**
- * Make the path relative to the given parent path.
- *
- * @param path The path to make relative
- * @param parent The parent path to make path relative to
- * @return 0 if path was made relative, GIT_ENOTFOUND
- * if there was not common root between the paths,
- * or <0.
- */
-extern int git_path_make_relative(git_buf *path, const char *parent);
-
-/**
- * Check if the given path contains the given file.
- *
- * @param dir Directory path that might contain file
- * @param file File name to look for in parent
- * @return true if file exists, false otherwise.
- */
-extern bool git_path_contains_file(git_buf *dir, const char *file);
-
-/**
- * Prepend base to unrooted path or just copy path over.
- *
- * This will optionally return the index into the path where the "root"
- * is, either the end of the base directory prefix or the path root.
- */
-extern int git_path_join_unrooted(
- git_buf *path_out, const char *path, const char *base, ssize_t *root_at);
-
-/**
- * Removes multiple occurrences of '/' in a row, squashing them into a
- * single '/'.
- */
-extern void git_path_squash_slashes(git_buf *path);
-
-/**
- * Clean up path, prepending base if it is not already rooted.
- */
-extern int git_path_prettify(git_buf *path_out, const char *path, const char *base);
-
-/**
- * Clean up path, prepending base if it is not already rooted and
- * appending a slash.
- */
-extern int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base);
-
-/**
- * Get a directory from a path.
- *
- * If path is a directory, this acts like `git_path_prettify_dir`
- * (cleaning up path and appending a '/'). If path is a normal file,
- * this prettifies it, then removed the filename a la dirname and
- * appends the trailing '/'. If the path does not exist, it is
- * treated like a regular filename.
- */
-extern int git_path_find_dir(git_buf *dir, const char *path, const char *base);
-
-/**
- * Resolve relative references within a path.
- *
- * This eliminates "./" and "../" relative references inside a path,
- * as well as condensing multiple slashes into single ones. It will
- * not touch the path before the "ceiling" length.
- *
- * Additionally, this will recognize an "c:/" drive prefix or a "xyz://" URL
- * prefix and not touch that part of the path.
- */
-extern int git_path_resolve_relative(git_buf *path, size_t ceiling);
-
-/**
- * Apply a relative path to base path.
- *
- * Note that the base path could be a filename or a URL and this
- * should still work. The relative path is walked segment by segment
- * with three rules: series of slashes will be condensed to a single
- * slash, "." will be eaten with no change, and ".." will remove a
- * segment from the base path.
- */
-extern int git_path_apply_relative(git_buf *target, const char *relpath);
-
-enum {
- GIT_PATH_DIR_IGNORE_CASE = (1u << 0),
- GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1),
- GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT = (1u << 2),
-};
-
-/**
- * Walk each directory entry, except '.' and '..', calling fn(state).
- *
- * @param pathbuf Buffer the function reads the initial directory
- * path from, and updates with each successive entry's name.
- * @param flags Combination of GIT_PATH_DIR flags.
- * @param callback Callback for each entry. Passed the `payload` and each
- * successive path inside the directory as a full path. This may
- * safely append text to the pathbuf if needed. Return non-zero to
- * cancel iteration (and return value will be propagated back).
- * @param payload Passed to callback as first argument.
- * @return 0 on success or error code from OS error or from callback
- */
-extern int git_path_direach(
- git_buf *pathbuf,
- uint32_t flags,
- int (*callback)(void *payload, git_buf *path),
- void *payload);
-
-/**
- * Sort function to order two paths
- */
-extern int git_path_cmp(
- const char *name1, size_t len1, int isdir1,
- const char *name2, size_t len2, int isdir2,
- int (*compare)(const char *, const char *, size_t));
-
-/**
- * Invoke callback up path directory by directory until the ceiling is
- * reached (inclusive of a final call at the root_path).
- *
- * Returning anything other than 0 from the callback function
- * will stop the iteration and propagate the error to the caller.
- *
- * @param pathbuf Buffer the function reads the directory from and
- * and updates with each successive name.
- * @param ceiling Prefix of path at which to stop walking up. If NULL,
- * this will walk all the way up to the root. If not a prefix of
- * pathbuf, the callback will be invoked a single time on the
- * original input path.
- * @param callback Function to invoke on each path. Passed the `payload`
- * and the buffer containing the current path. The path should not
- * be modified in any way. Return non-zero to stop iteration.
- * @param payload Passed to fn as the first ath.
- */
-extern int git_path_walk_up(
- git_buf *pathbuf,
- const char *ceiling,
- int (*callback)(void *payload, const char *path),
- void *payload);
-
-
-enum { GIT_PATH_NOTEQUAL = 0, GIT_PATH_EQUAL = 1, GIT_PATH_PREFIX = 2 };
-
-/*
- * Determines if a path is equal to or potentially a child of another.
- * @param parent The possible parent
- * @param child The possible child
- */
-GIT_INLINE(int) git_path_equal_or_prefixed(
- const char *parent,
- const char *child,
- ssize_t *prefixlen)
-{
- const char *p = parent, *c = child;
- int lastslash = 0;
-
- while (*p && *c) {
- lastslash = (*p == '/');
-
- if (*p++ != *c++)
- return GIT_PATH_NOTEQUAL;
- }
-
- if (*p != '\0')
- return GIT_PATH_NOTEQUAL;
-
- if (*c == '\0') {
- if (prefixlen)
- *prefixlen = p - parent;
-
- return GIT_PATH_EQUAL;
- }
-
- if (*c == '/' || lastslash) {
- if (prefixlen)
- *prefixlen = (p - parent) - lastslash;
-
- return GIT_PATH_PREFIX;
- }
-
- return GIT_PATH_NOTEQUAL;
-}
-
-/* translate errno to libgit2 error code and set error message */
-extern int git_path_set_error(
- int errno_value, const char *path, const char *action);
-
-/* check if non-ascii characters are present in filename */
-extern bool git_path_has_non_ascii(const char *path, size_t pathlen);
-
-#define GIT_PATH_REPO_ENCODING "UTF-8"
-
-#ifdef __APPLE__
-#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC"
-#else
-#define GIT_PATH_NATIVE_ENCODING "UTF-8"
-#endif
-
-#ifdef GIT_USE_ICONV
-
-#include <iconv.h>
-
-typedef struct {
- iconv_t map;
- git_buf buf;
-} git_path_iconv_t;
-
-#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_BUF_INIT }
-
-/* Init iconv data for converting decomposed UTF-8 to precomposed */
-extern int git_path_iconv_init_precompose(git_path_iconv_t *ic);
-
-/* Clear allocated iconv data */
-extern void git_path_iconv_clear(git_path_iconv_t *ic);
-
-/*
- * Rewrite `in` buffer using iconv map if necessary, replacing `in`
- * pointer internal iconv buffer if rewrite happened. The `in` pointer
- * will be left unchanged if no rewrite was needed.
- */
-extern int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen);
-
-#endif /* GIT_USE_ICONV */
-
-extern bool git_path_does_fs_decompose_unicode(const char *root);
-
-
-typedef struct git_path_diriter git_path_diriter;
-
-#if defined(GIT_WIN32) && !defined(__MINGW32__)
-
-struct git_path_diriter
-{
- git_win32_path path;
- size_t parent_len;
-
- git_buf path_utf8;
- size_t parent_utf8_len;
-
- HANDLE handle;
-
- unsigned int flags;
-
- WIN32_FIND_DATAW current;
- unsigned int needs_next;
-};
-
-#define GIT_PATH_DIRITER_INIT { {0}, 0, GIT_BUF_INIT, 0, INVALID_HANDLE_VALUE }
-
-#else
-
-struct git_path_diriter
-{
- git_buf path;
- size_t parent_len;
-
- unsigned int flags;
-
- DIR *dir;
-
-#ifdef GIT_USE_ICONV
- git_path_iconv_t ic;
-#endif
-};
-
-#define GIT_PATH_DIRITER_INIT { GIT_BUF_INIT }
-
-#endif
-
-/**
- * Initialize a directory iterator.
- *
- * @param diriter Pointer to a diriter structure that will be setup.
- * @param path The path that will be iterated over
- * @param flags Directory reader flags
- * @return 0 or an error code
- */
-extern int git_path_diriter_init(
- git_path_diriter *diriter,
- const char *path,
- unsigned int flags);
-
-/**
- * Advance the directory iterator. Will return GIT_ITEROVER when
- * the iteration has completed successfully.
- *
- * @param diriter The directory iterator
- * @return 0, GIT_ITEROVER, or an error code
- */
-extern int git_path_diriter_next(git_path_diriter *diriter);
-
-/**
- * Returns the file name of the current item in the iterator.
- *
- * @param out Pointer to store the path in
- * @param out_len Pointer to store the length of the path in
- * @param diriter The directory iterator
- * @return 0 or an error code
- */
-extern int git_path_diriter_filename(
- const char **out,
- size_t *out_len,
- git_path_diriter *diriter);
-
-/**
- * Returns the full path of the current item in the iterator; that
- * is the current filename plus the path of the directory that the
- * iterator was constructed with.
- *
- * @param out Pointer to store the path in
- * @param out_len Pointer to store the length of the path in
- * @param diriter The directory iterator
- * @return 0 or an error code
- */
-extern int git_path_diriter_fullpath(
- const char **out,
- size_t *out_len,
- git_path_diriter *diriter);
-
-/**
- * Performs an `lstat` on the current item in the iterator.
- *
- * @param out Pointer to store the stat data in
- * @param diriter The directory iterator
- * @return 0 or an error code
- */
-extern int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter);
-
-/**
- * Closes the directory iterator.
- *
- * @param diriter The directory iterator
- */
-extern void git_path_diriter_free(git_path_diriter *diriter);
-
-/**
- * Load all directory entries (except '.' and '..') into a vector.
- *
- * For cases where `git_path_direach()` is not appropriate, this
- * allows you to load the filenames in a directory into a vector
- * of strings. That vector can then be sorted, iterated, or whatever.
- * Remember to free alloc of the allocated strings when you are done.
- *
- * @param contents Vector to fill with directory entry names.
- * @param path The directory to read from.
- * @param prefix_len When inserting entries, the trailing part of path
- * will be prefixed after this length. I.e. given path "/a/b" and
- * prefix_len 3, the entries will look like "b/e1", "b/e2", etc.
- * @param flags Combination of GIT_PATH_DIR flags.
- */
-extern int git_path_dirload(
- git_vector *contents,
- const char *path,
- size_t prefix_len,
- uint32_t flags);
-
-
-/* Used for paths to repositories on the filesystem */
-extern bool git_path_is_local_file_url(const char *file_url);
-extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path);
-
-/* Flags to determine path validity in `git_path_isvalid` */
-#define GIT_PATH_REJECT_TRAVERSAL (1 << 0)
-#define GIT_PATH_REJECT_DOT_GIT (1 << 1)
-#define GIT_PATH_REJECT_SLASH (1 << 2)
-#define GIT_PATH_REJECT_BACKSLASH (1 << 3)
-#define GIT_PATH_REJECT_TRAILING_DOT (1 << 4)
-#define GIT_PATH_REJECT_TRAILING_SPACE (1 << 5)
-#define GIT_PATH_REJECT_TRAILING_COLON (1 << 6)
-#define GIT_PATH_REJECT_DOS_PATHS (1 << 7)
-#define GIT_PATH_REJECT_NT_CHARS (1 << 8)
-#define GIT_PATH_REJECT_DOT_GIT_LITERAL (1 << 9)
-#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 10)
-#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 11)
-
-/* Default path safety for writing files to disk: since we use the
- * Win32 "File Namespace" APIs ("\\?\") we need to protect from
- * paths that the normal Win32 APIs would not write.
- */
-#ifdef GIT_WIN32
-# define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \
- GIT_PATH_REJECT_TRAVERSAL | \
- GIT_PATH_REJECT_BACKSLASH | \
- GIT_PATH_REJECT_TRAILING_DOT | \
- GIT_PATH_REJECT_TRAILING_SPACE | \
- GIT_PATH_REJECT_TRAILING_COLON | \
- GIT_PATH_REJECT_DOS_PATHS | \
- GIT_PATH_REJECT_NT_CHARS
-#else
-# define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \
- GIT_PATH_REJECT_TRAVERSAL
-#endif
-
- /* Paths that should never be written into the working directory. */
-#define GIT_PATH_REJECT_WORKDIR_DEFAULTS \
- GIT_PATH_REJECT_FILESYSTEM_DEFAULTS | GIT_PATH_REJECT_DOT_GIT
-
-/* Paths that should never be written to the index. */
-#define GIT_PATH_REJECT_INDEX_DEFAULTS \
- GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT
-
-/*
- * Determine whether a path is a valid git path or not - this must not contain
- * a '.' or '..' component, or a component that is ".git" (in any case).
- *
- * `repo` is optional. If specified, it will be used to determine the short
- * path name to reject (if `GIT_PATH_REJECT_DOS_SHORTNAME` is specified),
- * in addition to the default of "git~1".
- */
-extern bool git_path_isvalid(
- git_repository *repo,
- const char *path,
- unsigned int flags);
-
-/**
- * Convert any backslashes into slashes
- */
-int git_path_normalize_slashes(git_buf *out, const char *path);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/pathspec.h"
-#include "git2/diff.h"
-#include "pathspec.h"
-#include "buf_text.h"
-#include "attr_file.h"
-#include "iterator.h"
-#include "repository.h"
-#include "index.h"
-#include "bitvec.h"
-#include "diff.h"
-
-/* what is the common non-wildcard prefix for all items in the pathspec */
-char *git_pathspec_prefix(const git_strarray *pathspec)
-{
- git_buf prefix = GIT_BUF_INIT;
- const char *scan;
-
- if (!pathspec || !pathspec->count ||
- git_buf_text_common_prefix(&prefix, pathspec) < 0)
- return NULL;
-
- /* diff prefix will only be leading non-wildcards */
- for (scan = prefix.ptr; *scan; ++scan) {
- if (git__iswildcard(*scan) &&
- (scan == prefix.ptr || (*(scan - 1) != '\\')))
- break;
- }
- git_buf_truncate(&prefix, scan - prefix.ptr);
-
- if (prefix.size <= 0) {
- git_buf_free(&prefix);
- return NULL;
- }
-
- git_buf_text_unescape(&prefix);
-
- return git_buf_detach(&prefix);
-}
-
-/* is there anything in the spec that needs to be filtered on */
-bool git_pathspec_is_empty(const git_strarray *pathspec)
-{
- size_t i;
-
- if (pathspec == NULL)
- return true;
-
- for (i = 0; i < pathspec->count; ++i) {
- const char *str = pathspec->strings[i];
-
- if (str && str[0])
- return false;
- }
-
- return true;
-}
-
-/* build a vector of fnmatch patterns to evaluate efficiently */
-int git_pathspec__vinit(
- git_vector *vspec, const git_strarray *strspec, git_pool *strpool)
-{
- size_t i;
-
- memset(vspec, 0, sizeof(*vspec));
-
- if (git_pathspec_is_empty(strspec))
- return 0;
-
- if (git_vector_init(vspec, strspec->count, NULL) < 0)
- return -1;
-
- for (i = 0; i < strspec->count; ++i) {
- int ret;
- const char *pattern = strspec->strings[i];
- git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
- if (!match)
- return -1;
-
- match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE |
- GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_NOLEADINGDIR;
-
- ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
- if (ret == GIT_ENOTFOUND) {
- git__free(match);
- continue;
- } else if (ret < 0) {
- git__free(match);
- return ret;
- }
-
- if (git_vector_insert(vspec, match) < 0)
- return -1;
- }
-
- return 0;
-}
-
-/* free data from the pathspec vector */
-void git_pathspec__vfree(git_vector *vspec)
-{
- git_vector_free_deep(vspec);
-}
-
-struct pathspec_match_context {
- int fnmatch_flags;
- int (*strcomp)(const char *, const char *);
- int (*strncomp)(const char *, const char *, size_t);
-};
-
-static void pathspec_match_context_init(
- struct pathspec_match_context *ctxt,
- bool disable_fnmatch,
- bool casefold)
-{
- if (disable_fnmatch)
- ctxt->fnmatch_flags = -1;
- else if (casefold)
- ctxt->fnmatch_flags = FNM_CASEFOLD;
- else
- ctxt->fnmatch_flags = 0;
-
- if (casefold) {
- ctxt->strcomp = git__strcasecmp;
- ctxt->strncomp = git__strncasecmp;
- } else {
- ctxt->strcomp = git__strcmp;
- ctxt->strncomp = git__strncmp;
- }
-}
-
-static int pathspec_match_one(
- const git_attr_fnmatch *match,
- struct pathspec_match_context *ctxt,
- const char *path)
-{
- int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : FNM_NOMATCH;
-
- if (result == FNM_NOMATCH)
- result = ctxt->strcomp(match->pattern, path) ? FNM_NOMATCH : 0;
-
- if (ctxt->fnmatch_flags >= 0 && result == FNM_NOMATCH)
- result = p_fnmatch(match->pattern, path, ctxt->fnmatch_flags);
-
- /* if we didn't match, look for exact dirname prefix match */
- if (result == FNM_NOMATCH &&
- (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
- ctxt->strncomp(path, match->pattern, match->length) == 0 &&
- path[match->length] == '/')
- result = 0;
-
- /* if we didn't match and this is a negative match, check for exact
- * match of filename with leading '!'
- */
- if (result == FNM_NOMATCH &&
- (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 &&
- *path == '!' &&
- ctxt->strncomp(path + 1, match->pattern, match->length) == 0 &&
- (!path[match->length + 1] || path[match->length + 1] == '/'))
- return 1;
-
- if (result == 0)
- return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? 0 : 1;
- return -1;
-}
-
-static int git_pathspec__match_at(
- size_t *matched_at,
- const git_vector *vspec,
- struct pathspec_match_context *ctxt,
- const char *path0,
- const char *path1)
-{
- int result = GIT_ENOTFOUND;
- size_t i = 0;
- const git_attr_fnmatch *match;
-
- git_vector_foreach(vspec, i, match) {
- if (path0 && (result = pathspec_match_one(match, ctxt, path0)) >= 0)
- break;
- if (path1 && (result = pathspec_match_one(match, ctxt, path1)) >= 0)
- break;
- }
-
- *matched_at = i;
- return result;
-}
-
-/* match a path against the vectorized pathspec */
-bool git_pathspec__match(
- const git_vector *vspec,
- const char *path,
- bool disable_fnmatch,
- bool casefold,
- const char **matched_pathspec,
- size_t *matched_at)
-{
- int result;
- size_t pos;
- struct pathspec_match_context ctxt;
-
- if (matched_pathspec)
- *matched_pathspec = NULL;
- if (matched_at)
- *matched_at = GIT_PATHSPEC_NOMATCH;
-
- if (!vspec || !vspec->length)
- return true;
-
- pathspec_match_context_init(&ctxt, disable_fnmatch, casefold);
-
- result = git_pathspec__match_at(&pos, vspec, &ctxt, path, NULL);
- if (result >= 0) {
- if (matched_pathspec) {
- const git_attr_fnmatch *match = git_vector_get(vspec, pos);
- *matched_pathspec = match->pattern;
- }
-
- if (matched_at)
- *matched_at = pos;
- }
-
- return (result > 0);
-}
-
-
-int git_pathspec__init(git_pathspec *ps, const git_strarray *paths)
-{
- int error = 0;
-
- memset(ps, 0, sizeof(*ps));
-
- ps->prefix = git_pathspec_prefix(paths);
- git_pool_init(&ps->pool, 1);
-
- if ((error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0)
- git_pathspec__clear(ps);
-
- return error;
-}
-
-void git_pathspec__clear(git_pathspec *ps)
-{
- git__free(ps->prefix);
- git_pathspec__vfree(&ps->pathspec);
- git_pool_clear(&ps->pool);
- memset(ps, 0, sizeof(*ps));
-}
-
-int git_pathspec_new(git_pathspec **out, const git_strarray *pathspec)
-{
- int error = 0;
- git_pathspec *ps = git__malloc(sizeof(git_pathspec));
- GITERR_CHECK_ALLOC(ps);
-
- if ((error = git_pathspec__init(ps, pathspec)) < 0) {
- git__free(ps);
- return error;
- }
-
- GIT_REFCOUNT_INC(ps);
- *out = ps;
- return 0;
-}
-
-static void pathspec_free(git_pathspec *ps)
-{
- git_pathspec__clear(ps);
- git__free(ps);
-}
-
-void git_pathspec_free(git_pathspec *ps)
-{
- if (!ps)
- return;
- GIT_REFCOUNT_DEC(ps, pathspec_free);
-}
-
-int git_pathspec_matches_path(
- const git_pathspec *ps, uint32_t flags, const char *path)
-{
- bool no_fnmatch = (flags & GIT_PATHSPEC_NO_GLOB) != 0;
- bool casefold = (flags & GIT_PATHSPEC_IGNORE_CASE) != 0;
-
- assert(ps && path);
-
- return (0 != git_pathspec__match(
- &ps->pathspec, path, no_fnmatch, casefold, NULL, NULL));
-}
-
-static void pathspec_match_free(git_pathspec_match_list *m)
-{
- if (!m)
- return;
-
- git_pathspec_free(m->pathspec);
- m->pathspec = NULL;
-
- git_array_clear(m->matches);
- git_array_clear(m->failures);
- git_pool_clear(&m->pool);
- git__free(m);
-}
-
-static git_pathspec_match_list *pathspec_match_alloc(
- git_pathspec *ps, int datatype)
-{
- git_pathspec_match_list *m = git__calloc(1, sizeof(git_pathspec_match_list));
- if (!m)
- return NULL;
-
- git_pool_init(&m->pool, 1);
-
- /* need to keep reference to pathspec and increment refcount because
- * failures array stores pointers to the pattern strings of the
- * pathspec that had no matches
- */
- GIT_REFCOUNT_INC(ps);
- m->pathspec = ps;
- m->datatype = datatype;
-
- return m;
-}
-
-GIT_INLINE(size_t) pathspec_mark_pattern(git_bitvec *used, size_t pos)
-{
- if (!git_bitvec_get(used, pos)) {
- git_bitvec_set(used, pos, true);
- return 1;
- }
-
- return 0;
-}
-
-static size_t pathspec_mark_remaining(
- git_bitvec *used,
- git_vector *patterns,
- struct pathspec_match_context *ctxt,
- size_t start,
- const char *path0,
- const char *path1)
-{
- size_t count = 0;
-
- if (path1 == path0)
- path1 = NULL;
-
- for (; start < patterns->length; ++start) {
- const git_attr_fnmatch *pat = git_vector_get(patterns, start);
-
- if (git_bitvec_get(used, start))
- continue;
-
- if (path0 && pathspec_match_one(pat, ctxt, path0) > 0)
- count += pathspec_mark_pattern(used, start);
- else if (path1 && pathspec_match_one(pat, ctxt, path1) > 0)
- count += pathspec_mark_pattern(used, start);
- }
-
- return count;
-}
-
-static int pathspec_build_failure_array(
- git_pathspec_string_array_t *failures,
- git_vector *patterns,
- git_bitvec *used,
- git_pool *pool)
-{
- size_t pos;
- char **failed;
- const git_attr_fnmatch *pat;
-
- for (pos = 0; pos < patterns->length; ++pos) {
- if (git_bitvec_get(used, pos))
- continue;
-
- if ((failed = git_array_alloc(*failures)) == NULL)
- return -1;
-
- pat = git_vector_get(patterns, pos);
-
- if ((*failed = git_pool_strdup(pool, pat->pattern)) == NULL)
- return -1;
- }
-
- return 0;
-}
-
-static int pathspec_match_from_iterator(
- git_pathspec_match_list **out,
- git_iterator *iter,
- uint32_t flags,
- git_pathspec *ps)
-{
- int error = 0;
- git_pathspec_match_list *m = NULL;
- const git_index_entry *entry = NULL;
- struct pathspec_match_context ctxt;
- git_vector *patterns = &ps->pathspec;
- bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
- bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
- size_t pos, used_ct = 0, found_files = 0;
- git_index *index = NULL;
- git_bitvec used_patterns;
- char **file;
-
- if (git_bitvec_init(&used_patterns, patterns->length) < 0)
- return -1;
-
- if (out) {
- *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_STRINGS);
- GITERR_CHECK_ALLOC(m);
- }
-
- if ((error = git_iterator_reset_range(iter, ps->prefix, ps->prefix)) < 0)
- goto done;
-
- if (git_iterator_type(iter) == GIT_ITERATOR_TYPE_WORKDIR &&
- (error = git_repository_index__weakptr(
- &index, git_iterator_owner(iter))) < 0)
- goto done;
-
- pathspec_match_context_init(
- &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
- git_iterator_ignore_case(iter));
-
- while (!(error = git_iterator_advance(&entry, iter))) {
- /* search for match with entry->path */
- int result = git_pathspec__match_at(
- &pos, patterns, &ctxt, entry->path, NULL);
-
- /* no matches for this path */
- if (result < 0)
- continue;
-
- /* if result was a negative pattern match, then don't list file */
- if (!result) {
- used_ct += pathspec_mark_pattern(&used_patterns, pos);
- continue;
- }
-
- /* check if path is ignored and untracked */
- if (index != NULL &&
- git_iterator_current_is_ignored(iter) &&
- git_index__find_pos(NULL, index, entry->path, 0, GIT_INDEX_STAGE_ANY) < 0)
- continue;
-
- /* mark the matched pattern as used */
- used_ct += pathspec_mark_pattern(&used_patterns, pos);
- ++found_files;
-
- /* if find_failures is on, check if any later patterns also match */
- if (find_failures && used_ct < patterns->length)
- used_ct += pathspec_mark_remaining(
- &used_patterns, patterns, &ctxt, pos + 1, entry->path, NULL);
-
- /* if only looking at failures, exit early or just continue */
- if (failures_only || !out) {
- if (used_ct == patterns->length)
- break;
- continue;
- }
-
- /* insert matched path into matches array */
- if ((file = (char **)git_array_alloc(m->matches)) == NULL ||
- (*file = git_pool_strdup(&m->pool, entry->path)) == NULL) {
- error = -1;
- goto done;
- }
- }
-
- if (error < 0 && error != GIT_ITEROVER)
- goto done;
- error = 0;
-
- /* insert patterns that had no matches into failures array */
- if (find_failures && used_ct < patterns->length &&
- (error = pathspec_build_failure_array(
- &m->failures, patterns, &used_patterns, &m->pool)) < 0)
- goto done;
-
- /* if every pattern failed to match, then we have failed */
- if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_files) {
- giterr_set(GITERR_INVALID, "No matching files were found");
- error = GIT_ENOTFOUND;
- }
-
-done:
- git_bitvec_free(&used_patterns);
-
- if (error < 0) {
- pathspec_match_free(m);
- if (out) *out = NULL;
- }
-
- return error;
-}
-
-static git_iterator_flag_t pathspec_match_iter_flags(uint32_t flags)
-{
- git_iterator_flag_t f = 0;
-
- if ((flags & GIT_PATHSPEC_IGNORE_CASE) != 0)
- f |= GIT_ITERATOR_IGNORE_CASE;
- else if ((flags & GIT_PATHSPEC_USE_CASE) != 0)
- f |= GIT_ITERATOR_DONT_IGNORE_CASE;
-
- return f;
-}
-
-int git_pathspec_match_workdir(
- git_pathspec_match_list **out,
- git_repository *repo,
- uint32_t flags,
- git_pathspec *ps)
-{
- git_iterator *iter;
- git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
- int error = 0;
-
- assert(repo);
-
- iter_opts.flags = pathspec_match_iter_flags(flags);
-
- if (!(error = git_iterator_for_workdir(&iter, repo, NULL, NULL, &iter_opts))) {
- error = pathspec_match_from_iterator(out, iter, flags, ps);
- git_iterator_free(iter);
- }
-
- return error;
-}
-
-int git_pathspec_match_index(
- git_pathspec_match_list **out,
- git_index *index,
- uint32_t flags,
- git_pathspec *ps)
-{
- git_iterator *iter;
- git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
- int error = 0;
-
- assert(index);
-
- iter_opts.flags = pathspec_match_iter_flags(flags);
-
- if (!(error = git_iterator_for_index(&iter, git_index_owner(index), index, &iter_opts))) {
- error = pathspec_match_from_iterator(out, iter, flags, ps);
- git_iterator_free(iter);
- }
-
- return error;
-}
-
-int git_pathspec_match_tree(
- git_pathspec_match_list **out,
- git_tree *tree,
- uint32_t flags,
- git_pathspec *ps)
-{
- git_iterator *iter;
- git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
- int error = 0;
-
- assert(tree);
-
- iter_opts.flags = pathspec_match_iter_flags(flags);
-
- if (!(error = git_iterator_for_tree(&iter, tree, &iter_opts))) {
- error = pathspec_match_from_iterator(out, iter, flags, ps);
- git_iterator_free(iter);
- }
-
- return error;
-}
-
-int git_pathspec_match_diff(
- git_pathspec_match_list **out,
- git_diff *diff,
- uint32_t flags,
- git_pathspec *ps)
-{
- int error = 0;
- git_pathspec_match_list *m = NULL;
- struct pathspec_match_context ctxt;
- git_vector *patterns = &ps->pathspec;
- bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
- bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
- size_t i, pos, used_ct = 0, found_deltas = 0;
- const git_diff_delta *delta, **match;
- git_bitvec used_patterns;
-
- assert(diff);
-
- if (git_bitvec_init(&used_patterns, patterns->length) < 0)
- return -1;
-
- if (out) {
- *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_DIFF);
- GITERR_CHECK_ALLOC(m);
- }
-
- pathspec_match_context_init(
- &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
- git_diff_is_sorted_icase(diff));
-
- git_vector_foreach(&diff->deltas, i, delta) {
- /* search for match with delta */
- int result = git_pathspec__match_at(
- &pos, patterns, &ctxt, delta->old_file.path, delta->new_file.path);
-
- /* no matches for this path */
- if (result < 0)
- continue;
-
- /* mark the matched pattern as used */
- used_ct += pathspec_mark_pattern(&used_patterns, pos);
-
- /* if result was a negative pattern match, then don't list file */
- if (!result)
- continue;
-
- ++found_deltas;
-
- /* if find_failures is on, check if any later patterns also match */
- if (find_failures && used_ct < patterns->length)
- used_ct += pathspec_mark_remaining(
- &used_patterns, patterns, &ctxt, pos + 1,
- delta->old_file.path, delta->new_file.path);
-
- /* if only looking at failures, exit early or just continue */
- if (failures_only || !out) {
- if (used_ct == patterns->length)
- break;
- continue;
- }
-
- /* insert matched delta into matches array */
- if (!(match = (const git_diff_delta **)git_array_alloc(m->matches))) {
- error = -1;
- goto done;
- } else {
- *match = delta;
- }
- }
-
- /* insert patterns that had no matches into failures array */
- if (find_failures && used_ct < patterns->length &&
- (error = pathspec_build_failure_array(
- &m->failures, patterns, &used_patterns, &m->pool)) < 0)
- goto done;
-
- /* if every pattern failed to match, then we have failed */
- if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_deltas) {
- giterr_set(GITERR_INVALID, "No matching deltas were found");
- error = GIT_ENOTFOUND;
- }
-
-done:
- git_bitvec_free(&used_patterns);
-
- if (error < 0) {
- pathspec_match_free(m);
- if (out) *out = NULL;
- }
-
- return error;
-}
-
-void git_pathspec_match_list_free(git_pathspec_match_list *m)
-{
- if (m)
- pathspec_match_free(m);
-}
-
-size_t git_pathspec_match_list_entrycount(
- const git_pathspec_match_list *m)
-{
- return m ? git_array_size(m->matches) : 0;
-}
-
-const char *git_pathspec_match_list_entry(
- const git_pathspec_match_list *m, size_t pos)
-{
- if (!m || m->datatype != PATHSPEC_DATATYPE_STRINGS ||
- !git_array_valid_index(m->matches, pos))
- return NULL;
-
- return *((const char **)git_array_get(m->matches, pos));
-}
-
-const git_diff_delta *git_pathspec_match_list_diff_entry(
- const git_pathspec_match_list *m, size_t pos)
-{
- if (!m || m->datatype != PATHSPEC_DATATYPE_DIFF ||
- !git_array_valid_index(m->matches, pos))
- return NULL;
-
- return *((const git_diff_delta **)git_array_get(m->matches, pos));
-}
-
-size_t git_pathspec_match_list_failed_entrycount(
- const git_pathspec_match_list *m)
-{
- return m ? git_array_size(m->failures) : 0;
-}
-
-const char * git_pathspec_match_list_failed_entry(
- const git_pathspec_match_list *m, size_t pos)
-{
- char **entry = m ? git_array_get(m->failures, pos) : NULL;
-
- return entry ? *entry : NULL;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_pathspec_h__
-#define INCLUDE_pathspec_h__
-
-#include "common.h"
-#include <git2/pathspec.h>
-#include "buffer.h"
-#include "vector.h"
-#include "pool.h"
-#include "array.h"
-
-/* public compiled pathspec */
-struct git_pathspec {
- git_refcount rc;
- char *prefix;
- git_vector pathspec;
- git_pool pool;
-};
-
-enum {
- PATHSPEC_DATATYPE_STRINGS = 0,
- PATHSPEC_DATATYPE_DIFF = 1,
-};
-
-typedef git_array_t(char *) git_pathspec_string_array_t;
-
-/* public interface to pathspec matching */
-struct git_pathspec_match_list {
- git_pathspec *pathspec;
- git_array_t(void *) matches;
- git_pathspec_string_array_t failures;
- git_pool pool;
- int datatype;
-};
-
-/* what is the common non-wildcard prefix for all items in the pathspec */
-extern char *git_pathspec_prefix(const git_strarray *pathspec);
-
-/* is there anything in the spec that needs to be filtered on */
-extern bool git_pathspec_is_empty(const git_strarray *pathspec);
-
-/* build a vector of fnmatch patterns to evaluate efficiently */
-extern int git_pathspec__vinit(
- git_vector *vspec, const git_strarray *strspec, git_pool *strpool);
-
-/* free data from the pathspec vector */
-extern void git_pathspec__vfree(git_vector *vspec);
-
-#define GIT_PATHSPEC_NOMATCH ((size_t)-1)
-
-/*
- * Match a path against the vectorized pathspec.
- * The matched pathspec is passed back into the `matched_pathspec` parameter,
- * unless it is passed as NULL by the caller.
- */
-extern bool git_pathspec__match(
- const git_vector *vspec,
- const char *path,
- bool disable_fnmatch,
- bool casefold,
- const char **matched_pathspec,
- size_t *matched_at);
-
-/* easy pathspec setup */
-
-extern int git_pathspec__init(git_pathspec *ps, const git_strarray *paths);
-
-extern void git_pathspec__clear(git_pathspec *ps);
-
-#endif
+++ /dev/null
-#include "pool.h"
-#include "posix.h"
-#ifndef GIT_WIN32
-#include <unistd.h>
-#endif
-
-struct git_pool_page {
- git_pool_page *next;
- uint32_t size;
- uint32_t avail;
- GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8);
-};
-
-static void *pool_alloc_page(git_pool *pool, uint32_t size);
-
-uint32_t git_pool__system_page_size(void)
-{
- static uint32_t size = 0;
-
- if (!size) {
- size_t page_size;
- if (git__page_size(&page_size) < 0)
- page_size = 4096;
- /* allow space for malloc overhead */
- size = page_size - (2 * sizeof(void *)) - sizeof(git_pool_page);
- }
-
- return size;
-}
-
-#ifndef GIT_DEBUG_POOL
-void git_pool_init(git_pool *pool, uint32_t item_size)
-{
- assert(pool);
- assert(item_size >= 1);
-
- memset(pool, 0, sizeof(git_pool));
- pool->item_size = item_size;
- pool->page_size = git_pool__system_page_size();
-}
-
-void git_pool_clear(git_pool *pool)
-{
- git_pool_page *scan, *next;
-
- for (scan = pool->pages; scan != NULL; scan = next) {
- next = scan->next;
- git__free(scan);
- }
-
- pool->pages = NULL;
-}
-
-static void *pool_alloc_page(git_pool *pool, uint32_t size)
-{
- git_pool_page *page;
- const uint32_t new_page_size = (size <= pool->page_size) ? pool->page_size : size;
- size_t alloc_size;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) ||
- !(page = git__malloc(alloc_size)))
- return NULL;
-
- page->size = new_page_size;
- page->avail = new_page_size - size;
- page->next = pool->pages;
-
- pool->pages = page;
-
- return page->data;
-}
-
-static void *pool_alloc(git_pool *pool, uint32_t size)
-{
- git_pool_page *page = pool->pages;
- void *ptr = NULL;
-
- if (!page || page->avail < size)
- return pool_alloc_page(pool, size);
-
- ptr = &page->data[page->size - page->avail];
- page->avail -= size;
-
- return ptr;
-}
-
-uint32_t git_pool__open_pages(git_pool *pool)
-{
- uint32_t ct = 0;
- git_pool_page *scan;
- for (scan = pool->pages; scan != NULL; scan = scan->next) ct++;
- return ct;
-}
-
-bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
-{
- git_pool_page *scan;
- for (scan = pool->pages; scan != NULL; scan = scan->next)
- if ((void *)scan->data <= ptr &&
- (void *)(((char *)scan->data) + scan->size) > ptr)
- return true;
- return false;
-}
-
-#else
-
-static int git_pool__ptr_cmp(const void * a, const void * b)
-{
- if(a > b) {
- return 1;
- }
- if(a < b) {
- return -1;
- }
- else {
- return 0;
- }
-}
-
-void git_pool_init(git_pool *pool, uint32_t item_size)
-{
- assert(pool);
- assert(item_size >= 1);
-
- memset(pool, 0, sizeof(git_pool));
- pool->item_size = item_size;
- pool->page_size = git_pool__system_page_size();
- git_vector_init(&pool->allocations, 100, git_pool__ptr_cmp);
-}
-
-void git_pool_clear(git_pool *pool)
-{
- git_vector_free_deep(&pool->allocations);
-}
-
-static void *pool_alloc(git_pool *pool, uint32_t size) {
- void *ptr = NULL;
- if((ptr = git__malloc(size)) == NULL) {
- return NULL;
- }
- git_vector_insert_sorted(&pool->allocations, ptr, NULL);
- return ptr;
-}
-
-bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
-{
- size_t pos;
- return git_vector_bsearch(&pos, &pool->allocations, ptr) != GIT_ENOTFOUND;
-}
-#endif
-
-void git_pool_swap(git_pool *a, git_pool *b)
-{
- git_pool temp;
-
- if (a == b)
- return;
-
- memcpy(&temp, a, sizeof(temp));
- memcpy(a, b, sizeof(temp));
- memcpy(b, &temp, sizeof(temp));
-}
-
-static uint32_t alloc_size(git_pool *pool, uint32_t count)
-{
- const uint32_t align = sizeof(void *) - 1;
-
- if (pool->item_size > 1) {
- const uint32_t item_size = (pool->item_size + align) & ~align;
- return item_size * count;
- }
-
- return (count + align) & ~align;
-}
-
-void *git_pool_malloc(git_pool *pool, uint32_t items)
-{
- return pool_alloc(pool, alloc_size(pool, items));
-}
-
-void *git_pool_mallocz(git_pool *pool, uint32_t items)
-{
- const uint32_t size = alloc_size(pool, items);
- void *ptr = pool_alloc(pool, size);
- if (ptr)
- memset(ptr, 0x0, size);
- return ptr;
-}
-
-char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
-{
- char *ptr = NULL;
-
- assert(pool && str && pool->item_size == sizeof(char));
-
- if ((uint32_t)(n + 1) < n)
- return NULL;
-
- if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) {
- memcpy(ptr, str, n);
- ptr[n] = '\0';
- }
-
- return ptr;
-}
-
-char *git_pool_strdup(git_pool *pool, const char *str)
-{
- assert(pool && str && pool->item_size == sizeof(char));
- return git_pool_strndup(pool, str, strlen(str));
-}
-
-char *git_pool_strdup_safe(git_pool *pool, const char *str)
-{
- return str ? git_pool_strdup(pool, str) : NULL;
-}
-
-char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
-{
- void *ptr;
- size_t len_a, len_b;
-
- assert(pool && pool->item_size == sizeof(char));
-
- len_a = a ? strlen(a) : 0;
- len_b = b ? strlen(b) : 0;
-
- if ((ptr = git_pool_malloc(pool, (uint32_t)(len_a + len_b + 1))) != NULL) {
- if (len_a)
- memcpy(ptr, a, len_a);
- if (len_b)
- memcpy(((char *)ptr) + len_a, b, len_b);
- *(((char *)ptr) + len_a + len_b) = '\0';
- }
- return ptr;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_pool_h__
-#define INCLUDE_pool_h__
-
-#include "common.h"
-#include "vector.h"
-
-typedef struct git_pool_page git_pool_page;
-
-#ifndef GIT_DEBUG_POOL
-/**
- * Chunked allocator.
- *
- * A `git_pool` can be used when you want to cheaply allocate
- * multiple items of the same type and are willing to free them
- * all together with a single call. The two most common cases
- * are a set of fixed size items (such as lots of OIDs) or a
- * bunch of strings.
- *
- * Internally, a `git_pool` allocates pages of memory and then
- * deals out blocks from the trailing unused portion of each page.
- * The pages guarantee that the number of actual allocations done
- * will be much smaller than the number of items needed.
- *
- * For examples of how to set up a `git_pool` see `git_pool_init`.
- */
-typedef struct {
- git_pool_page *pages; /* allocated pages */
- uint32_t item_size; /* size of single alloc unit in bytes */
- uint32_t page_size; /* size of page in bytes */
-} git_pool;
-
-#define GIT_POOL_INIT { NULL, 0, 0 }
-
-#else
-
-/**
- * Debug chunked allocator.
- *
- * Acts just like `git_pool` but instead of actually pooling allocations it
- * passes them through to `git__malloc`. This makes it possible to easily debug
- * systems that use `git_pool` using valgrind.
- *
- * In order to track allocations during the lifetime of the pool we use a
- * `git_vector`. When the pool is deallocated everything in the vector is
- * freed.
- *
- * `API is exactly the same as the standard `git_pool` with one exception.
- * Since we aren't allocating pages to hand out in chunks we can't easily
- * implement `git_pool__open_pages`.
- */
-typedef struct {
- git_vector allocations;
- uint32_t item_size;
- uint32_t page_size;
-} git_pool;
-
-#define GIT_POOL_INIT { GIT_VECTOR_INIT, 0, 0 }
-
-#endif
-
-/**
- * Initialize a pool.
- *
- * To allocation strings, use like this:
- *
- * git_pool_init(&string_pool, 1);
- * my_string = git_pool_strdup(&string_pool, your_string);
- *
- * To allocate items of fixed size, use like this:
- *
- * git_pool_init(&pool, sizeof(item));
- * my_item = git_pool_malloc(&pool, 1);
- *
- * Of course, you can use this in other ways, but those are the
- * two most common patterns.
- */
-extern void git_pool_init(git_pool *pool, uint32_t item_size);
-
-/**
- * Free all items in pool
- */
-extern void git_pool_clear(git_pool *pool);
-
-/**
- * Swap two pools with one another
- */
-extern void git_pool_swap(git_pool *a, git_pool *b);
-
-/**
- * Allocate space for one or more items from a pool.
- */
-extern void *git_pool_malloc(git_pool *pool, uint32_t items);
-extern void *git_pool_mallocz(git_pool *pool, uint32_t items);
-
-/**
- * Allocate space and duplicate string data into it.
- *
- * This is allowed only for pools with item_size == sizeof(char)
- */
-extern char *git_pool_strndup(git_pool *pool, const char *str, size_t n);
-
-/**
- * Allocate space and duplicate a string into it.
- *
- * This is allowed only for pools with item_size == sizeof(char)
- */
-extern char *git_pool_strdup(git_pool *pool, const char *str);
-
-/**
- * Allocate space and duplicate a string into it, NULL is no error.
- *
- * This is allowed only for pools with item_size == sizeof(char)
- */
-extern char *git_pool_strdup_safe(git_pool *pool, const char *str);
-
-/**
- * Allocate space for the concatenation of two strings.
- *
- * This is allowed only for pools with item_size == sizeof(char)
- */
-extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b);
-
-/*
- * Misc utilities
- */
-#ifndef GIT_DEBUG_POOL
-extern uint32_t git_pool__open_pages(git_pool *pool);
-#endif
-extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "posix.h"
-#include "path.h"
-#include <stdio.h>
-#include <ctype.h>
-
-#ifndef GIT_WIN32
-
-#ifdef NO_ADDRINFO
-
-int p_getaddrinfo(
- const char *host,
- const char *port,
- struct addrinfo *hints,
- struct addrinfo **info)
-{
- struct addrinfo *ainfo, *ai;
- int p = 0;
-
- GIT_UNUSED(hints);
-
- if ((ainfo = malloc(sizeof(struct addrinfo))) == NULL)
- return -1;
-
- if ((ainfo->ai_hostent = gethostbyname(host)) == NULL) {
- free(ainfo);
- return -2;
- }
-
- ainfo->ai_servent = getservbyname(port, 0);
-
- if (ainfo->ai_servent)
- ainfo->ai_port = ainfo->ai_servent->s_port;
- else
- ainfo->ai_port = atol(port);
-
- memcpy(&ainfo->ai_addr_in.sin_addr,
- ainfo->ai_hostent->h_addr_list[0],
- ainfo->ai_hostent->h_length);
-
- ainfo->ai_protocol = 0;
- ainfo->ai_socktype = hints->ai_socktype;
- ainfo->ai_family = ainfo->ai_hostent->h_addrtype;
- ainfo->ai_addr_in.sin_family = ainfo->ai_family;
- ainfo->ai_addr_in.sin_port = ainfo->ai_port;
- ainfo->ai_addr = (struct addrinfo *)&ainfo->ai_addr_in;
- ainfo->ai_addrlen = sizeof(struct sockaddr_in);
-
- *info = ainfo;
-
- if (ainfo->ai_hostent->h_addr_list[1] == NULL) {
- ainfo->ai_next = NULL;
- return 0;
- }
-
- ai = ainfo;
-
- for (p = 1; ainfo->ai_hostent->h_addr_list[p] != NULL; p++) {
- if (!(ai->ai_next = malloc(sizeof(struct addrinfo)))) {
- p_freeaddrinfo(ainfo);
- return -1;
- }
- memcpy(ai->ai_next, ainfo, sizeof(struct addrinfo));
- memcpy(&ai->ai_next->ai_addr_in.sin_addr,
- ainfo->ai_hostent->h_addr_list[p],
- ainfo->ai_hostent->h_length);
- ai->ai_next->ai_addr = (struct addrinfo *)&ai->ai_next->ai_addr_in;
- ai = ai->ai_next;
- }
-
- ai->ai_next = NULL;
- return 0;
-}
-
-void p_freeaddrinfo(struct addrinfo *info)
-{
- struct addrinfo *p, *next;
-
- p = info;
-
- while(p != NULL) {
- next = p->ai_next;
- free(p);
- p = next;
- }
-}
-
-const char *p_gai_strerror(int ret)
-{
- switch(ret) {
- case -1: return "Out of memory"; break;
- case -2: return "Address lookup failed"; break;
- default: return "Unknown error"; break;
- }
-}
-
-#endif /* NO_ADDRINFO */
-
-int p_open(const char *path, volatile int flags, ...)
-{
- mode_t mode = 0;
-
- if (flags & O_CREAT) {
- va_list arg_list;
-
- va_start(arg_list, flags);
- mode = (mode_t)va_arg(arg_list, int);
- va_end(arg_list);
- }
-
- return open(path, flags | O_BINARY | O_CLOEXEC, mode);
-}
-
-int p_creat(const char *path, mode_t mode)
-{
- return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC, mode);
-}
-
-int p_getcwd(char *buffer_out, size_t size)
-{
- char *cwd_buffer;
-
- assert(buffer_out && size > 0);
-
- cwd_buffer = getcwd(buffer_out, size);
-
- if (cwd_buffer == NULL)
- return -1;
-
- git_path_mkposix(buffer_out);
- git_path_string_to_dir(buffer_out, size); /* append trailing slash */
-
- return 0;
-}
-
-int p_rename(const char *from, const char *to)
-{
- if (!link(from, to)) {
- p_unlink(from);
- return 0;
- }
-
- if (!rename(from, to))
- return 0;
-
- return -1;
-}
-
-#endif /* GIT_WIN32 */
-
-ssize_t p_read(git_file fd, void *buf, size_t cnt)
-{
- char *b = buf;
-
- if (!git__is_ssizet(cnt)) {
-#ifdef GIT_WIN32
- SetLastError(ERROR_INVALID_PARAMETER);
-#endif
- errno = EINVAL;
- return -1;
- }
-
- while (cnt) {
- ssize_t r;
-#ifdef GIT_WIN32
- r = read(fd, b, cnt > INT_MAX ? INT_MAX : (unsigned int)cnt);
-#else
- r = read(fd, b, cnt);
-#endif
- if (r < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return -1;
- }
- if (!r)
- break;
- cnt -= r;
- b += r;
- }
- return (b - (char *)buf);
-}
-
-int p_write(git_file fd, const void *buf, size_t cnt)
-{
- const char *b = buf;
-
- while (cnt) {
- ssize_t r;
-#ifdef GIT_WIN32
- assert((size_t)((unsigned int)cnt) == cnt);
- r = write(fd, b, (unsigned int)cnt);
-#else
- r = write(fd, b, cnt);
-#endif
- if (r < 0) {
- if (errno == EINTR || GIT_ISBLOCKED(errno))
- continue;
- return -1;
- }
- if (!r) {
- errno = EPIPE;
- return -1;
- }
- cnt -= r;
- b += r;
- }
- return 0;
-}
-
-#ifdef NO_MMAP
-
-#include "map.h"
-
-int git__page_size(size_t *page_size)
-{
- /* dummy; here we don't need any alignment anyway */
- *page_size = 4096;
- return 0;
-}
-
-int git__mmap_alignment(size_t *alignment)
-{
- /* dummy; here we don't need any alignment anyway */
- *alignment = 4096;
- return 0;
-}
-
-
-int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
-{
- GIT_MMAP_VALIDATE(out, len, prot, flags);
-
- out->data = NULL;
- out->len = 0;
-
- if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) {
- giterr_set(GITERR_OS, "Trying to map shared-writeable");
- return -1;
- }
-
- out->data = malloc(len);
- GITERR_CHECK_ALLOC(out->data);
-
- if (!git__is_ssizet(len) ||
- (p_lseek(fd, offset, SEEK_SET) < 0) ||
- (p_read(fd, out->data, len) != (ssize_t)len)) {
- giterr_set(GITERR_OS, "mmap emulation failed");
- return -1;
- }
-
- out->len = len;
- return 0;
-}
-
-int p_munmap(git_map *map)
-{
- assert(map != NULL);
- free(map->data);
-
- return 0;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_posix_h__
-#define INCLUDE_posix_h__
-
-#include "common.h"
-#include <fcntl.h>
-#include <time.h>
-#include "fnmatch.h"
-
-/* stat: file mode type testing macros */
-#ifndef S_IFGITLINK
-#define S_IFGITLINK 0160000
-#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
-#endif
-
-#ifndef S_IFLNK
-#define S_IFLNK 0120000
-#undef _S_IFLNK
-#define _S_IFLNK S_IFLNK
-#endif
-
-#ifndef S_IXUSR
-#define S_IXUSR 00100
-#endif
-
-#ifndef S_ISLNK
-#define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
-#endif
-
-#ifndef S_ISDIR
-#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
-#endif
-
-#ifndef S_ISREG
-#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
-#endif
-
-#ifndef S_ISFIFO
-#define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO)
-#endif
-
-/* if S_ISGID is not defined, then don't try to set it */
-#ifndef S_ISGID
-#define S_ISGID 0
-#endif
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
-#endif
-
-/* access() mode parameter #defines */
-#ifndef F_OK
-#define F_OK 0 /* existence check */
-#endif
-#ifndef W_OK
-#define W_OK 2 /* write mode check */
-#endif
-#ifndef R_OK
-#define R_OK 4 /* read mode check */
-#endif
-
-/* Determine whether an errno value indicates that a read or write failed
- * because the descriptor is blocked.
- */
-#if defined(EWOULDBLOCK)
-#define GIT_ISBLOCKED(e) ((e) == EAGAIN || (e) == EWOULDBLOCK)
-#else
-#define GIT_ISBLOCKED(e) ((e) == EAGAIN)
-#endif
-
-/* define some standard errnos that the runtime may be missing. for example,
- * mingw lacks EAFNOSUPPORT. */
-#ifndef EAFNOSUPPORT
-#define EAFNOSUPPORT (INT_MAX-1)
-#endif
-
-typedef int git_file;
-
-/**
- * Standard POSIX Methods
- *
- * All the methods starting with the `p_` prefix are
- * direct ports of the standard POSIX methods.
- *
- * Some of the methods are slightly wrapped to provide
- * saner defaults. Some of these methods are emulated
- * in Windows platforms.
- *
- * Use your manpages to check the docs on these.
- */
-
-extern ssize_t p_read(git_file fd, void *buf, size_t cnt);
-extern int p_write(git_file fd, const void *buf, size_t cnt);
-
-#define p_close(fd) close(fd)
-#define p_umask(m) umask(m)
-
-extern int p_open(const char *path, int flags, ...);
-extern int p_creat(const char *path, mode_t mode);
-extern int p_getcwd(char *buffer_out, size_t size);
-extern int p_rename(const char *from, const char *to);
-
-extern int git__page_size(size_t *page_size);
-extern int git__mmap_alignment(size_t *page_size);
-
-/**
- * Platform-dependent methods
- */
-#ifdef GIT_WIN32
-# include "win32/posix.h"
-#else
-# include "unix/posix.h"
-#endif
-
-#include "strnlen.h"
-
-#ifdef NO_READDIR_R
-GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
-{
- GIT_UNUSED(entry);
- *result = readdir(dirp);
- return 0;
-}
-#else /* NO_READDIR_R */
-# define p_readdir_r(d,e,r) readdir_r(d,e,r)
-#endif
-
-#ifdef NO_ADDRINFO
-# include <netdb.h>
-struct addrinfo {
- struct hostent *ai_hostent;
- struct servent *ai_servent;
- struct sockaddr_in ai_addr_in;
- struct sockaddr *ai_addr;
- size_t ai_addrlen;
- int ai_family;
- int ai_socktype;
- int ai_protocol;
- long ai_port;
- struct addrinfo *ai_next;
-};
-
-extern int p_getaddrinfo(const char *host, const char *port,
- struct addrinfo *hints, struct addrinfo **info);
-extern void p_freeaddrinfo(struct addrinfo *info);
-extern const char *p_gai_strerror(int ret);
-#else
-# define p_getaddrinfo(a, b, c, d) getaddrinfo(a, b, c, d)
-# define p_freeaddrinfo(a) freeaddrinfo(a)
-# define p_gai_strerror(c) gai_strerror(c)
-#endif /* NO_ADDRINFO */
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "pqueue.h"
-#include "util.h"
-
-#define PQUEUE_LCHILD_OF(I) (((I)<<1)+1)
-#define PQUEUE_RCHILD_OF(I) (((I)<<1)+2)
-#define PQUEUE_PARENT_OF(I) (((I)-1)>>1)
-
-int git_pqueue_init(
- git_pqueue *pq,
- uint32_t flags,
- size_t init_size,
- git_vector_cmp cmp)
-{
- int error = git_vector_init(pq, init_size, cmp);
-
- if (!error) {
- /* mix in our flags */
- pq->flags |= flags;
-
- /* if fixed size heap, pretend vector is exactly init_size elements */
- if ((flags & GIT_PQUEUE_FIXED_SIZE) && init_size > 0)
- pq->_alloc_size = init_size;
- }
-
- return error;
-}
-
-static void pqueue_up(git_pqueue *pq, size_t el)
-{
- size_t parent_el = PQUEUE_PARENT_OF(el);
- void *kid = git_vector_get(pq, el);
-
- while (el > 0) {
- void *parent = pq->contents[parent_el];
-
- if (pq->_cmp(parent, kid) <= 0)
- break;
-
- pq->contents[el] = parent;
-
- el = parent_el;
- parent_el = PQUEUE_PARENT_OF(el);
- }
-
- pq->contents[el] = kid;
-}
-
-static void pqueue_down(git_pqueue *pq, size_t el)
-{
- void *parent = git_vector_get(pq, el), *kid, *rkid;
-
- while (1) {
- size_t kid_el = PQUEUE_LCHILD_OF(el);
-
- if ((kid = git_vector_get(pq, kid_el)) == NULL)
- break;
-
- if ((rkid = git_vector_get(pq, kid_el + 1)) != NULL &&
- pq->_cmp(kid, rkid) > 0) {
- kid = rkid;
- kid_el += 1;
- }
-
- if (pq->_cmp(parent, kid) <= 0)
- break;
-
- pq->contents[el] = kid;
- el = kid_el;
- }
-
- pq->contents[el] = parent;
-}
-
-int git_pqueue_insert(git_pqueue *pq, void *item)
-{
- int error = 0;
-
- /* if heap is full, pop the top element if new one should replace it */
- if ((pq->flags & GIT_PQUEUE_FIXED_SIZE) != 0 &&
- pq->length >= pq->_alloc_size)
- {
- /* skip this item if below min item in heap or if
- * we do not have a comparison function */
- if (!pq->_cmp || pq->_cmp(item, git_vector_get(pq, 0)) <= 0)
- return 0;
- /* otherwise remove the min item before inserting new */
- (void)git_pqueue_pop(pq);
- }
-
- if (!(error = git_vector_insert(pq, item)) && pq->_cmp)
- pqueue_up(pq, pq->length - 1);
-
- return error;
-}
-
-void *git_pqueue_pop(git_pqueue *pq)
-{
- void *rval;
-
- if (!pq->_cmp) {
- rval = git_vector_last(pq);
- } else {
- rval = git_pqueue_get(pq, 0);
- }
-
- if (git_pqueue_size(pq) > 1 && pq->_cmp) {
- /* move last item to top of heap, shrink, and push item down */
- pq->contents[0] = git_vector_last(pq);
- git_vector_pop(pq);
- pqueue_down(pq, 0);
- } else {
- /* all we need to do is shrink the heap in this case */
- git_vector_pop(pq);
- }
-
- return rval;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_pqueue_h__
-#define INCLUDE_pqueue_h__
-
-#include "vector.h"
-
-typedef git_vector git_pqueue;
-
-enum {
- /* flag meaning: don't grow heap, keep highest values only */
- GIT_PQUEUE_FIXED_SIZE = (GIT_VECTOR_FLAG_MAX << 1),
-};
-
-/**
- * Initialize priority queue
- *
- * @param pq The priority queue struct to initialize
- * @param flags Flags (see above) to control queue behavior
- * @param init_size The initial queue size
- * @param cmp The entry priority comparison function
- * @return 0 on success, <0 on error
- */
-extern int git_pqueue_init(
- git_pqueue *pq,
- uint32_t flags,
- size_t init_size,
- git_vector_cmp cmp);
-
-#define git_pqueue_free git_vector_free
-#define git_pqueue_clear git_vector_clear
-#define git_pqueue_size git_vector_length
-#define git_pqueue_get git_vector_get
-#define git_pqueue_reverse git_vector_reverse
-
-/**
- * Insert a new item into the queue
- *
- * @param pq The priority queue
- * @param item Pointer to the item data
- * @return 0 on success, <0 on failure
- */
-extern int git_pqueue_insert(git_pqueue *pq, void *item);
-
-/**
- * Remove the top item in the priority queue
- *
- * @param pq The priority queue
- * @return item from heap on success, NULL if queue is empty
- */
-extern void *git_pqueue_pop(git_pqueue *pq);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "git2/proxy.h"
-
-int git_proxy_init_options(git_proxy_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_proxy_options, GIT_PROXY_OPTIONS_INIT);
- return 0;
-}
-
-int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src)
-{
- if (!src) {
- git_proxy_init_options(tgt, GIT_PROXY_OPTIONS_VERSION);
- return 0;
- }
-
- memcpy(tgt, src, sizeof(git_proxy_options));
- if (src->url) {
- tgt->url = git__strdup(src->url);
- GITERR_CHECK_ALLOC(tgt->url);
- }
-
- return 0;
-}
+++ /dev/null
-/*
-* Copyright (C) the libgit2 contributors. All rights reserved.
-*
-* This file is part of libgit2, distributed under the GNU GPL v2 with
-* a Linking Exception. For full terms see the included COPYING file.
-*/
-#ifndef INCLUDE_proxy_h__
-#define INCLUDE_proxy_h__
-
-#include "git2/proxy.h"
-
-extern int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src);
-
-#endif
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2.h"
-
-#include "common.h"
-#include "pack.h"
-#include "pack-objects.h"
-#include "remote.h"
-#include "vector.h"
-#include "push.h"
-#include "tree.h"
-
-static int push_spec_rref_cmp(const void *a, const void *b)
-{
- const push_spec *push_spec_a = a, *push_spec_b = b;
-
- return strcmp(push_spec_a->refspec.dst, push_spec_b->refspec.dst);
-}
-
-static int push_status_ref_cmp(const void *a, const void *b)
-{
- const push_status *push_status_a = a, *push_status_b = b;
-
- return strcmp(push_status_a->ref, push_status_b->ref);
-}
-
-int git_push_new(git_push **out, git_remote *remote)
-{
- git_push *p;
-
- *out = NULL;
-
- p = git__calloc(1, sizeof(*p));
- GITERR_CHECK_ALLOC(p);
-
- p->repo = remote->repo;
- p->remote = remote;
- p->report_status = 1;
- p->pb_parallelism = 1;
-
- if (git_vector_init(&p->specs, 0, push_spec_rref_cmp) < 0) {
- git__free(p);
- return -1;
- }
-
- if (git_vector_init(&p->status, 0, push_status_ref_cmp) < 0) {
- git_vector_free(&p->specs);
- git__free(p);
- return -1;
- }
-
- if (git_vector_init(&p->updates, 0, NULL) < 0) {
- git_vector_free(&p->status);
- git_vector_free(&p->specs);
- git__free(p);
- return -1;
- }
-
- *out = p;
- return 0;
-}
-
-int git_push_set_options(git_push *push, const git_push_options *opts)
-{
- if (!push || !opts)
- return -1;
-
- GITERR_CHECK_VERSION(opts, GIT_PUSH_OPTIONS_VERSION, "git_push_options");
-
- push->pb_parallelism = opts->pb_parallelism;
- push->custom_headers = &opts->custom_headers;
-
- return 0;
-}
-
-static void free_refspec(push_spec *spec)
-{
- if (spec == NULL)
- return;
-
- git_refspec__free(&spec->refspec);
- git__free(spec);
-}
-
-static int check_rref(char *ref)
-{
- if (git__prefixcmp(ref, "refs/")) {
- giterr_set(GITERR_INVALID, "Not a valid reference '%s'", ref);
- return -1;
- }
-
- return 0;
-}
-
-static int check_lref(git_push *push, char *ref)
-{
- /* lref must be resolvable to an existing object */
- git_object *obj;
- int error = git_revparse_single(&obj, push->repo, ref);
- git_object_free(obj);
-
- if (!error)
- return 0;
-
- if (error == GIT_ENOTFOUND)
- giterr_set(GITERR_REFERENCE,
- "src refspec '%s' does not match any existing object", ref);
- else
- giterr_set(GITERR_INVALID, "Not a valid reference '%s'", ref);
- return -1;
-}
-
-static int parse_refspec(git_push *push, push_spec **spec, const char *str)
-{
- push_spec *s;
-
- *spec = NULL;
-
- s = git__calloc(1, sizeof(*s));
- GITERR_CHECK_ALLOC(s);
-
- if (git_refspec__parse(&s->refspec, str, false) < 0) {
- giterr_set(GITERR_INVALID, "invalid refspec %s", str);
- goto on_error;
- }
-
- if (s->refspec.src && s->refspec.src[0] != '\0' &&
- check_lref(push, s->refspec.src) < 0) {
- goto on_error;
- }
-
- if (check_rref(s->refspec.dst) < 0)
- goto on_error;
-
- *spec = s;
- return 0;
-
-on_error:
- free_refspec(s);
- return -1;
-}
-
-int git_push_add_refspec(git_push *push, const char *refspec)
-{
- push_spec *spec;
-
- if (parse_refspec(push, &spec, refspec) < 0 ||
- git_vector_insert(&push->specs, spec) < 0)
- return -1;
-
- return 0;
-}
-
-int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks)
-{
- git_buf remote_ref_name = GIT_BUF_INIT;
- size_t i, j;
- git_refspec *fetch_spec;
- push_spec *push_spec = NULL;
- git_reference *remote_ref;
- push_status *status;
- int error = 0;
-
- git_vector_foreach(&push->status, i, status) {
- int fire_callback = 1;
-
- /* Skip unsuccessful updates which have non-empty messages */
- if (status->msg)
- continue;
-
- /* Find the corresponding remote ref */
- fetch_spec = git_remote__matching_refspec(push->remote, status->ref);
- if (!fetch_spec)
- continue;
-
- if ((error = git_refspec_transform(&remote_ref_name, fetch_spec, status->ref)) < 0)
- goto on_error;
-
- /* Find matching push ref spec */
- git_vector_foreach(&push->specs, j, push_spec) {
- if (!strcmp(push_spec->refspec.dst, status->ref))
- break;
- }
-
- /* Could not find the corresponding push ref spec for this push update */
- if (j == push->specs.length)
- continue;
-
- /* Update the remote ref */
- if (git_oid_iszero(&push_spec->loid)) {
- error = git_reference_lookup(&remote_ref, push->remote->repo, git_buf_cstr(&remote_ref_name));
-
- if (error >= 0) {
- error = git_reference_delete(remote_ref);
- git_reference_free(remote_ref);
- }
- } else {
- error = git_reference_create(NULL, push->remote->repo,
- git_buf_cstr(&remote_ref_name), &push_spec->loid, 1,
- "update by push");
- }
-
- if (error < 0) {
- if (error != GIT_ENOTFOUND)
- goto on_error;
-
- giterr_clear();
- fire_callback = 0;
- }
-
- if (fire_callback && callbacks && callbacks->update_tips) {
- error = callbacks->update_tips(git_buf_cstr(&remote_ref_name),
- &push_spec->roid, &push_spec->loid, callbacks->payload);
-
- if (error < 0)
- goto on_error;
- }
- }
-
- error = 0;
-
-on_error:
- git_buf_free(&remote_ref_name);
- return error;
-}
-
-/**
- * Insert all tags until we find a non-tag object, which is returned
- * in `out`.
- */
-static int enqueue_tag(git_object **out, git_push *push, git_oid *id)
-{
- git_object *obj = NULL, *target = NULL;
- int error;
-
- if ((error = git_object_lookup(&obj, push->repo, id, GIT_OBJ_TAG)) < 0)
- return error;
-
- while (git_object_type(obj) == GIT_OBJ_TAG) {
- if ((error = git_packbuilder_insert(push->pb, git_object_id(obj), NULL)) < 0)
- break;
-
- if ((error = git_tag_target(&target, (git_tag *) obj)) < 0)
- break;
-
- git_object_free(obj);
- obj = target;
- }
-
- if (error < 0)
- git_object_free(obj);
- else
- *out = obj;
-
- return error;
-}
-
-static int revwalk(git_vector *commits, git_push *push)
-{
- git_remote_head *head;
- push_spec *spec;
- git_revwalk *rw;
- git_oid oid;
- unsigned int i;
- int error = -1;
-
- if (git_revwalk_new(&rw, push->repo) < 0)
- return -1;
-
- git_revwalk_sorting(rw, GIT_SORT_TIME);
-
- git_vector_foreach(&push->specs, i, spec) {
- git_otype type;
- size_t size;
-
- if (git_oid_iszero(&spec->loid))
- /*
- * Delete reference on remote side;
- * nothing to do here.
- */
- continue;
-
- if (git_oid_equal(&spec->loid, &spec->roid))
- continue; /* up-to-date */
-
- if (git_odb_read_header(&size, &type, push->repo->_odb, &spec->loid) < 0)
- goto on_error;
-
- if (type == GIT_OBJ_TAG) {
- git_object *target;
-
- if ((error = enqueue_tag(&target, push, &spec->loid)) < 0)
- goto on_error;
-
- if (git_object_type(target) == GIT_OBJ_COMMIT) {
- if (git_revwalk_push(rw, git_object_id(target)) < 0) {
- git_object_free(target);
- goto on_error;
- }
- } else {
- if (git_packbuilder_insert(
- push->pb, git_object_id(target), NULL) < 0) {
- git_object_free(target);
- goto on_error;
- }
- }
- git_object_free(target);
- } else if (git_revwalk_push(rw, &spec->loid) < 0)
- goto on_error;
-
- if (!spec->refspec.force) {
- git_oid base;
-
- if (git_oid_iszero(&spec->roid))
- continue;
-
- if (!git_odb_exists(push->repo->_odb, &spec->roid)) {
- giterr_set(GITERR_REFERENCE,
- "Cannot push because a reference that you are trying to update on the remote contains commits that are not present locally.");
- error = GIT_ENONFASTFORWARD;
- goto on_error;
- }
-
- error = git_merge_base(&base, push->repo,
- &spec->loid, &spec->roid);
-
- if (error == GIT_ENOTFOUND ||
- (!error && !git_oid_equal(&base, &spec->roid))) {
- giterr_set(GITERR_REFERENCE,
- "Cannot push non-fastforwardable reference");
- error = GIT_ENONFASTFORWARD;
- goto on_error;
- }
-
- if (error < 0)
- goto on_error;
- }
- }
-
- git_vector_foreach(&push->remote->refs, i, head) {
- if (git_oid_iszero(&head->oid))
- continue;
-
- /* TODO */
- git_revwalk_hide(rw, &head->oid);
- }
-
- while ((error = git_revwalk_next(&oid, rw)) == 0) {
- git_oid *o = git__malloc(GIT_OID_RAWSZ);
- if (!o) {
- error = -1;
- goto on_error;
- }
- git_oid_cpy(o, &oid);
- if ((error = git_vector_insert(commits, o)) < 0)
- goto on_error;
- }
-
-on_error:
- git_revwalk_free(rw);
- return error == GIT_ITEROVER ? 0 : error;
-}
-
-static int enqueue_object(
- const git_tree_entry *entry,
- git_packbuilder *pb)
-{
- switch (git_tree_entry_type(entry)) {
- case GIT_OBJ_COMMIT:
- return 0;
- case GIT_OBJ_TREE:
- return git_packbuilder_insert_tree(pb, entry->oid);
- default:
- return git_packbuilder_insert(pb, entry->oid, entry->filename);
- }
-}
-
-static int queue_differences(
- git_tree *base,
- git_tree *delta,
- git_packbuilder *pb)
-{
- git_tree *b_child = NULL, *d_child = NULL;
- size_t b_length = git_tree_entrycount(base);
- size_t d_length = git_tree_entrycount(delta);
- size_t i = 0, j = 0;
- int error;
-
- while (i < b_length && j < d_length) {
- const git_tree_entry *b_entry = git_tree_entry_byindex(base, i);
- const git_tree_entry *d_entry = git_tree_entry_byindex(delta, j);
- int cmp = 0;
-
- if (!git_oid__cmp(b_entry->oid, d_entry->oid))
- goto loop;
-
- cmp = strcmp(b_entry->filename, d_entry->filename);
-
- /* If the entries are both trees and they have the same name but are
- * different, then we'll recurse after adding the right-hand entry */
- if (!cmp &&
- git_tree_entry__is_tree(b_entry) &&
- git_tree_entry__is_tree(d_entry)) {
- /* Add the right-hand entry */
- if ((error = git_packbuilder_insert(pb, d_entry->oid,
- d_entry->filename)) < 0)
- goto on_error;
-
- /* Acquire the subtrees and recurse */
- if ((error = git_tree_lookup(&b_child,
- git_tree_owner(base), b_entry->oid)) < 0 ||
- (error = git_tree_lookup(&d_child,
- git_tree_owner(delta), d_entry->oid)) < 0 ||
- (error = queue_differences(b_child, d_child, pb)) < 0)
- goto on_error;
-
- git_tree_free(b_child); b_child = NULL;
- git_tree_free(d_child); d_child = NULL;
- }
- /* If the object is new or different in the right-hand tree,
- * then enumerate it */
- else if (cmp >= 0 &&
- (error = enqueue_object(d_entry, pb)) < 0)
- goto on_error;
-
- loop:
- if (cmp <= 0) i++;
- if (cmp >= 0) j++;
- }
-
- /* Drain the right-hand tree of entries */
- for (; j < d_length; j++)
- if ((error = enqueue_object(git_tree_entry_byindex(delta, j), pb)) < 0)
- goto on_error;
-
- error = 0;
-
-on_error:
- if (b_child)
- git_tree_free(b_child);
-
- if (d_child)
- git_tree_free(d_child);
-
- return error;
-}
-
-static int queue_objects(git_push *push)
-{
- git_vector commits = GIT_VECTOR_INIT;
- git_oid *oid;
- size_t i;
- unsigned j;
- int error;
-
- if ((error = revwalk(&commits, push)) < 0)
- goto on_error;
-
- git_vector_foreach(&commits, i, oid) {
- git_commit *parent = NULL, *commit;
- git_tree *tree = NULL, *ptree = NULL;
- size_t parentcount;
-
- if ((error = git_commit_lookup(&commit, push->repo, oid)) < 0)
- goto on_error;
-
- /* Insert the commit */
- if ((error = git_packbuilder_insert(push->pb, oid, NULL)) < 0)
- goto loop_error;
-
- parentcount = git_commit_parentcount(commit);
-
- if (!parentcount) {
- if ((error = git_packbuilder_insert_tree(push->pb,
- git_commit_tree_id(commit))) < 0)
- goto loop_error;
- } else {
- if ((error = git_tree_lookup(&tree, push->repo,
- git_commit_tree_id(commit))) < 0 ||
- (error = git_packbuilder_insert(push->pb,
- git_commit_tree_id(commit), NULL)) < 0)
- goto loop_error;
-
- /* For each parent, add the items which are different */
- for (j = 0; j < parentcount; j++) {
- if ((error = git_commit_parent(&parent, commit, j)) < 0 ||
- (error = git_commit_tree(&ptree, parent)) < 0 ||
- (error = queue_differences(ptree, tree, push->pb)) < 0)
- goto loop_error;
-
- git_tree_free(ptree); ptree = NULL;
- git_commit_free(parent); parent = NULL;
- }
- }
-
- error = 0;
-
- loop_error:
- if (tree)
- git_tree_free(tree);
-
- if (ptree)
- git_tree_free(ptree);
-
- if (parent)
- git_commit_free(parent);
-
- git_commit_free(commit);
-
- if (error < 0)
- goto on_error;
- }
-
- error = 0;
-
-on_error:
- git_vector_free_deep(&commits);
- return error;
-}
-
-static int add_update(git_push *push, push_spec *spec)
-{
- git_push_update *u = git__calloc(1, sizeof(git_push_update));
- GITERR_CHECK_ALLOC(u);
-
- u->src_refname = git__strdup(spec->refspec.src);
- GITERR_CHECK_ALLOC(u->src_refname);
-
- u->dst_refname = git__strdup(spec->refspec.dst);
- GITERR_CHECK_ALLOC(u->dst_refname);
-
- git_oid_cpy(&u->src, &spec->roid);
- git_oid_cpy(&u->dst, &spec->loid);
-
- return git_vector_insert(&push->updates, u);
-}
-
-static int calculate_work(git_push *push)
-{
- git_remote_head *head;
- push_spec *spec;
- unsigned int i, j;
-
- /* Update local and remote oids*/
-
- git_vector_foreach(&push->specs, i, spec) {
- if (spec->refspec.src && spec->refspec.src[0]!= '\0') {
- /* This is a create or update. Local ref must exist. */
- if (git_reference_name_to_id(
- &spec->loid, push->repo, spec->refspec.src) < 0) {
- giterr_set(GITERR_REFERENCE, "No such reference '%s'", spec->refspec.src);
- return -1;
- }
- }
-
- /* Remote ref may or may not (e.g. during create) already exist. */
- git_vector_foreach(&push->remote->refs, j, head) {
- if (!strcmp(spec->refspec.dst, head->name)) {
- git_oid_cpy(&spec->roid, &head->oid);
- break;
- }
- }
-
- if (add_update(push, spec) < 0)
- return -1;
- }
-
- return 0;
-}
-
-static int do_push(git_push *push, const git_remote_callbacks *callbacks)
-{
- int error = 0;
- git_transport *transport = push->remote->transport;
-
- if (!transport->push) {
- giterr_set(GITERR_NET, "Remote transport doesn't support push");
- error = -1;
- goto on_error;
- }
-
- /*
- * A pack-file MUST be sent if either create or update command
- * is used, even if the server already has all the necessary
- * objects. In this case the client MUST send an empty pack-file.
- */
-
- if ((error = git_packbuilder_new(&push->pb, push->repo)) < 0)
- goto on_error;
-
- git_packbuilder_set_threads(push->pb, push->pb_parallelism);
-
- if (callbacks && callbacks->pack_progress)
- if ((error = git_packbuilder_set_callbacks(push->pb, callbacks->pack_progress, callbacks->payload)) < 0)
- goto on_error;
-
- if ((error = calculate_work(push)) < 0)
- goto on_error;
-
- if (callbacks && callbacks->push_negotiation &&
- (error = callbacks->push_negotiation((const git_push_update **) push->updates.contents,
- push->updates.length, callbacks->payload)) < 0)
- goto on_error;
-
- if ((error = queue_objects(push)) < 0 ||
- (error = transport->push(transport, push, callbacks)) < 0)
- goto on_error;
-
-on_error:
- git_packbuilder_free(push->pb);
- return error;
-}
-
-static int filter_refs(git_remote *remote)
-{
- const git_remote_head **heads;
- size_t heads_len, i;
-
- git_vector_clear(&remote->refs);
-
- if (git_remote_ls(&heads, &heads_len, remote) < 0)
- return -1;
-
- for (i = 0; i < heads_len; i++) {
- if (git_vector_insert(&remote->refs, (void *)heads[i]) < 0)
- return -1;
- }
-
- return 0;
-}
-
-int git_push_finish(git_push *push, const git_remote_callbacks *callbacks)
-{
- int error;
-
- if (!git_remote_connected(push->remote) &&
- (error = git_remote_connect(push->remote, GIT_DIRECTION_PUSH, callbacks, NULL, push->custom_headers)) < 0)
- return error;
-
- if ((error = filter_refs(push->remote)) < 0 ||
- (error = do_push(push, callbacks)) < 0)
- return error;
-
- if (!push->unpack_ok) {
- error = -1;
- giterr_set(GITERR_NET, "unpacking the sent packfile failed on the remote");
- }
-
- return error;
-}
-
-int git_push_status_foreach(git_push *push,
- int (*cb)(const char *ref, const char *msg, void *data),
- void *data)
-{
- push_status *status;
- unsigned int i;
-
- git_vector_foreach(&push->status, i, status) {
- int error = cb(status->ref, status->msg, data);
- if (error)
- return giterr_set_after_callback(error);
- }
-
- return 0;
-}
-
-void git_push_status_free(push_status *status)
-{
- if (status == NULL)
- return;
-
- git__free(status->msg);
- git__free(status->ref);
- git__free(status);
-}
-
-void git_push_free(git_push *push)
-{
- push_spec *spec;
- push_status *status;
- git_push_update *update;
- unsigned int i;
-
- if (push == NULL)
- return;
-
- git_vector_foreach(&push->specs, i, spec) {
- free_refspec(spec);
- }
- git_vector_free(&push->specs);
-
- git_vector_foreach(&push->status, i, status) {
- git_push_status_free(status);
- }
- git_vector_free(&push->status);
-
- git_vector_foreach(&push->updates, i, update) {
- git__free(update->src_refname);
- git__free(update->dst_refname);
- git__free(update);
- }
- git_vector_free(&push->updates);
-
- git__free(push);
-}
-
-int git_push_init_options(git_push_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_push_options, GIT_PUSH_OPTIONS_INIT);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_push_h__
-#define INCLUDE_push_h__
-
-#include "git2.h"
-#include "refspec.h"
-
-typedef struct push_spec {
- struct git_refspec refspec;
-
- git_oid loid;
- git_oid roid;
-} push_spec;
-
-typedef struct push_status {
- bool ok;
-
- char *ref;
- char *msg;
-} push_status;
-
-struct git_push {
- git_repository *repo;
- git_packbuilder *pb;
- git_remote *remote;
- git_vector specs;
- git_vector updates;
- bool report_status;
-
- /* report-status */
- bool unpack_ok;
- git_vector status;
-
- /* options */
- unsigned pb_parallelism;
- const git_strarray *custom_headers;
-};
-
-/**
- * Free the given push status object
- *
- * @param status The push status object
- */
-void git_push_status_free(push_status *status);
-
-/**
- * Create a new push object
- *
- * @param out New push object
- * @param remote Remote instance
- *
- * @return 0 or an error code
- */
-int git_push_new(git_push **out, git_remote *remote);
-
-/**
- * Set options on a push object
- *
- * @param push The push object
- * @param opts The options to set on the push object
- *
- * @return 0 or an error code
- */
-int git_push_set_options(
- git_push *push,
- const git_push_options *opts);
-
-/**
- * Add a refspec to be pushed
- *
- * @param push The push object
- * @param refspec Refspec string
- *
- * @return 0 or an error code
- */
-int git_push_add_refspec(git_push *push, const char *refspec);
-
-/**
- * Update remote tips after a push
- *
- * @param push The push object
- * @param callbacks the callbacks to use for this connection
- *
- * @return 0 or an error code
- */
-int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks);
-
-/**
- * Perform the push
- *
- * This function will return an error in case of a protocol error or
- * the server being unable to unpack the data we sent.
- *
- * The return value does not reflect whether the server accepted or
- * refused any reference updates. Use `git_push_status_foreach()` in
- * order to find out which updates were accepted or rejected.
- *
- * @param push The push object
- * @param callbacks the callbacks to use for this connection
- *
- * @return 0 or an error code
- */
-int git_push_finish(git_push *push, const git_remote_callbacks *callbacks);
-
-/**
- * Invoke callback `cb' on each status entry
- *
- * For each of the updated references, we receive a status report in the
- * form of `ok refs/heads/master` or `ng refs/heads/master <msg>`.
- * `msg != NULL` means the reference has not been updated for the given
- * reason.
- *
- * Return a non-zero value from the callback to stop the loop.
- *
- * @param push The push object
- * @param cb The callback to call on each object
- * @param data The payload passed to the callback
- *
- * @return 0 on success, non-zero callback return value, or error code
- */
-int git_push_status_foreach(git_push *push,
- int (*cb)(const char *ref, const char *msg, void *data),
- void *data);
-
-/**
- * Free the given push object
- *
- * @param push The push object
- */
-void git_push_free(git_push *push);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "buffer.h"
-#include "repository.h"
-#include "posix.h"
-#include "filebuf.h"
-#include "merge.h"
-#include "array.h"
-#include "config.h"
-#include "annotated_commit.h"
-#include "index.h"
-
-#include <git2/types.h>
-#include <git2/annotated_commit.h>
-#include <git2/rebase.h>
-#include <git2/commit.h>
-#include <git2/reset.h>
-#include <git2/revwalk.h>
-#include <git2/notes.h>
-
-#define REBASE_APPLY_DIR "rebase-apply"
-#define REBASE_MERGE_DIR "rebase-merge"
-
-#define HEAD_NAME_FILE "head-name"
-#define ORIG_HEAD_FILE "orig-head"
-#define HEAD_FILE "head"
-#define ONTO_FILE "onto"
-#define ONTO_NAME_FILE "onto_name"
-#define QUIET_FILE "quiet"
-
-#define MSGNUM_FILE "msgnum"
-#define END_FILE "end"
-#define CMT_FILE_FMT "cmt.%" PRIuZ
-#define CURRENT_FILE "current"
-#define REWRITTEN_FILE "rewritten"
-
-#define ORIG_DETACHED_HEAD "detached HEAD"
-
-#define NOTES_DEFAULT_REF NULL
-
-#define REBASE_DIR_MODE 0777
-#define REBASE_FILE_MODE 0666
-
-typedef enum {
- GIT_REBASE_TYPE_NONE = 0,
- GIT_REBASE_TYPE_APPLY = 1,
- GIT_REBASE_TYPE_MERGE = 2,
- GIT_REBASE_TYPE_INTERACTIVE = 3,
-} git_rebase_type_t;
-
-struct git_rebase {
- git_repository *repo;
-
- git_rebase_options options;
-
- git_rebase_type_t type;
- char *state_path;
-
- int head_detached : 1,
- inmemory : 1,
- quiet : 1,
- started : 1;
-
- git_array_t(git_rebase_operation) operations;
- size_t current;
-
- /* Used by in-memory rebase */
- git_index *index;
- git_commit *last_commit;
-
- /* Used by regular (not in-memory) merge-style rebase */
- git_oid orig_head_id;
- char *orig_head_name;
-
- git_oid onto_id;
- char *onto_name;
-};
-
-#define GIT_REBASE_STATE_INIT {0}
-
-static int rebase_state_type(
- git_rebase_type_t *type_out,
- char **path_out,
- git_repository *repo)
-{
- git_buf path = GIT_BUF_INIT;
- git_rebase_type_t type = GIT_REBASE_TYPE_NONE;
-
- if (git_buf_joinpath(&path, repo->path_repository, REBASE_APPLY_DIR) < 0)
- return -1;
-
- if (git_path_isdir(git_buf_cstr(&path))) {
- type = GIT_REBASE_TYPE_APPLY;
- goto done;
- }
-
- git_buf_clear(&path);
- if (git_buf_joinpath(&path, repo->path_repository, REBASE_MERGE_DIR) < 0)
- return -1;
-
- if (git_path_isdir(git_buf_cstr(&path))) {
- type = GIT_REBASE_TYPE_MERGE;
- goto done;
- }
-
-done:
- *type_out = type;
-
- if (type != GIT_REBASE_TYPE_NONE && path_out)
- *path_out = git_buf_detach(&path);
-
- git_buf_free(&path);
-
- return 0;
-}
-
-GIT_INLINE(int) rebase_readfile(
- git_buf *out,
- git_buf *state_path,
- const char *filename)
-{
- size_t state_path_len = state_path->size;
- int error;
-
- git_buf_clear(out);
-
- if ((error = git_buf_joinpath(state_path, state_path->ptr, filename)) < 0 ||
- (error = git_futils_readbuffer(out, state_path->ptr)) < 0)
- goto done;
-
- git_buf_rtrim(out);
-
-done:
- git_buf_truncate(state_path, state_path_len);
- return error;
-}
-
-GIT_INLINE(int) rebase_readint(
- size_t *out, git_buf *asc_out, git_buf *state_path, const char *filename)
-{
- int32_t num;
- const char *eol;
- int error = 0;
-
- if ((error = rebase_readfile(asc_out, state_path, filename)) < 0)
- return error;
-
- if (git__strtol32(&num, asc_out->ptr, &eol, 10) < 0 || num < 0 || *eol) {
- giterr_set(GITERR_REBASE, "The file '%s' contains an invalid numeric value", filename);
- return -1;
- }
-
- *out = (size_t) num;
-
- return 0;
-}
-
-GIT_INLINE(int) rebase_readoid(
- git_oid *out, git_buf *str_out, git_buf *state_path, const char *filename)
-{
- int error;
-
- if ((error = rebase_readfile(str_out, state_path, filename)) < 0)
- return error;
-
- if (str_out->size != GIT_OID_HEXSZ || git_oid_fromstr(out, str_out->ptr) < 0) {
- giterr_set(GITERR_REBASE, "The file '%s' contains an invalid object ID", filename);
- return -1;
- }
-
- return 0;
-}
-
-static git_rebase_operation *rebase_operation_alloc(
- git_rebase *rebase,
- git_rebase_operation_t type,
- git_oid *id,
- const char *exec)
-{
- git_rebase_operation *operation;
-
- assert((type == GIT_REBASE_OPERATION_EXEC) == !id);
- assert((type == GIT_REBASE_OPERATION_EXEC) == !!exec);
-
- if ((operation = git_array_alloc(rebase->operations)) == NULL)
- return NULL;
-
- operation->type = type;
- git_oid_cpy((git_oid *)&operation->id, id);
- operation->exec = exec;
-
- return operation;
-}
-
-static int rebase_open_merge(git_rebase *rebase)
-{
- git_buf state_path = GIT_BUF_INIT, buf = GIT_BUF_INIT, cmt = GIT_BUF_INIT;
- git_oid id;
- git_rebase_operation *operation;
- size_t i, msgnum = 0, end;
- int error;
-
- if ((error = git_buf_puts(&state_path, rebase->state_path)) < 0)
- goto done;
-
- /* Read 'msgnum' if it exists (otherwise, let msgnum = 0) */
- if ((error = rebase_readint(&msgnum, &buf, &state_path, MSGNUM_FILE)) < 0 &&
- error != GIT_ENOTFOUND)
- goto done;
-
- if (msgnum) {
- rebase->started = 1;
- rebase->current = msgnum - 1;
- }
-
- /* Read 'end' */
- if ((error = rebase_readint(&end, &buf, &state_path, END_FILE)) < 0)
- goto done;
-
- /* Read 'current' if it exists */
- if ((error = rebase_readoid(&id, &buf, &state_path, CURRENT_FILE)) < 0 &&
- error != GIT_ENOTFOUND)
- goto done;
-
- /* Read cmt.* */
- git_array_init_to_size(rebase->operations, end);
- GITERR_CHECK_ARRAY(rebase->operations);
-
- for (i = 0; i < end; i++) {
- git_buf_clear(&cmt);
-
- if ((error = git_buf_printf(&cmt, "cmt.%" PRIuZ, (i+1))) < 0 ||
- (error = rebase_readoid(&id, &buf, &state_path, cmt.ptr)) < 0)
- goto done;
-
- operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL);
- GITERR_CHECK_ALLOC(operation);
- }
-
- /* Read 'onto_name' */
- if ((error = rebase_readfile(&buf, &state_path, ONTO_NAME_FILE)) < 0)
- goto done;
-
- rebase->onto_name = git_buf_detach(&buf);
-
-done:
- git_buf_free(&cmt);
- git_buf_free(&state_path);
- git_buf_free(&buf);
-
- return error;
-}
-
-static int rebase_alloc(git_rebase **out, const git_rebase_options *rebase_opts)
-{
- git_rebase *rebase = git__calloc(1, sizeof(git_rebase));
- GITERR_CHECK_ALLOC(rebase);
-
- *out = NULL;
-
- if (rebase_opts)
- memcpy(&rebase->options, rebase_opts, sizeof(git_rebase_options));
- else
- git_rebase_init_options(&rebase->options, GIT_REBASE_OPTIONS_VERSION);
-
- if (rebase_opts && rebase_opts->rewrite_notes_ref) {
- rebase->options.rewrite_notes_ref = git__strdup(rebase_opts->rewrite_notes_ref);
- GITERR_CHECK_ALLOC(rebase->options.rewrite_notes_ref);
- }
-
- if ((rebase->options.checkout_options.checkout_strategy & (GIT_CHECKOUT_SAFE | GIT_CHECKOUT_FORCE)) == 0)
- rebase->options.checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
-
- *out = rebase;
-
- return 0;
-}
-
-static int rebase_check_versions(const git_rebase_options *given_opts)
-{
- GITERR_CHECK_VERSION(given_opts, GIT_REBASE_OPTIONS_VERSION, "git_rebase_options");
-
- if (given_opts)
- GITERR_CHECK_VERSION(&given_opts->checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options");
-
- return 0;
-}
-
-int git_rebase_open(
- git_rebase **out,
- git_repository *repo,
- const git_rebase_options *given_opts)
-{
- git_rebase *rebase;
- git_buf path = GIT_BUF_INIT, orig_head_name = GIT_BUF_INIT,
- orig_head_id = GIT_BUF_INIT, onto_id = GIT_BUF_INIT;
- int state_path_len, error;
-
- assert(repo);
-
- if ((error = rebase_check_versions(given_opts)) < 0)
- return error;
-
- if (rebase_alloc(&rebase, given_opts) < 0)
- return -1;
-
- rebase->repo = repo;
-
- if ((error = rebase_state_type(&rebase->type, &rebase->state_path, repo)) < 0)
- goto done;
-
- if (rebase->type == GIT_REBASE_TYPE_NONE) {
- giterr_set(GITERR_REBASE, "There is no rebase in progress");
- error = GIT_ENOTFOUND;
- goto done;
- }
-
- if ((error = git_buf_puts(&path, rebase->state_path)) < 0)
- goto done;
-
- state_path_len = git_buf_len(&path);
-
- if ((error = git_buf_joinpath(&path, path.ptr, HEAD_NAME_FILE)) < 0 ||
- (error = git_futils_readbuffer(&orig_head_name, path.ptr)) < 0)
- goto done;
-
- git_buf_rtrim(&orig_head_name);
-
- if (strcmp(ORIG_DETACHED_HEAD, orig_head_name.ptr) == 0)
- rebase->head_detached = 1;
-
- git_buf_truncate(&path, state_path_len);
-
- if ((error = git_buf_joinpath(&path, path.ptr, ORIG_HEAD_FILE)) < 0)
- goto done;
-
- if (!git_path_isfile(path.ptr)) {
- /* Previous versions of git.git used 'head' here; support that. */
- git_buf_truncate(&path, state_path_len);
-
- if ((error = git_buf_joinpath(&path, path.ptr, HEAD_FILE)) < 0)
- goto done;
- }
-
- if ((error = git_futils_readbuffer(&orig_head_id, path.ptr)) < 0)
- goto done;
-
- git_buf_rtrim(&orig_head_id);
-
- if ((error = git_oid_fromstr(&rebase->orig_head_id, orig_head_id.ptr)) < 0)
- goto done;
-
- git_buf_truncate(&path, state_path_len);
-
- if ((error = git_buf_joinpath(&path, path.ptr, ONTO_FILE)) < 0 ||
- (error = git_futils_readbuffer(&onto_id, path.ptr)) < 0)
- goto done;
-
- git_buf_rtrim(&onto_id);
-
- if ((error = git_oid_fromstr(&rebase->onto_id, onto_id.ptr)) < 0)
- goto done;
-
- if (!rebase->head_detached)
- rebase->orig_head_name = git_buf_detach(&orig_head_name);
-
- switch (rebase->type) {
- case GIT_REBASE_TYPE_INTERACTIVE:
- giterr_set(GITERR_REBASE, "Interactive rebase is not supported");
- error = -1;
- break;
- case GIT_REBASE_TYPE_MERGE:
- error = rebase_open_merge(rebase);
- break;
- case GIT_REBASE_TYPE_APPLY:
- giterr_set(GITERR_REBASE, "Patch application rebase is not supported");
- error = -1;
- break;
- default:
- abort();
- }
-
-done:
- if (error == 0)
- *out = rebase;
- else
- git_rebase_free(rebase);
-
- git_buf_free(&path);
- git_buf_free(&orig_head_name);
- git_buf_free(&orig_head_id);
- git_buf_free(&onto_id);
- return error;
-}
-
-static int rebase_cleanup(git_rebase *rebase)
-{
- if (!rebase || rebase->inmemory)
- return 0;
-
- return git_path_isdir(rebase->state_path) ?
- git_futils_rmdir_r(rebase->state_path, NULL, GIT_RMDIR_REMOVE_FILES) :
- 0;
-}
-
-static int rebase_setupfile(git_rebase *rebase, const char *filename, int flags, const char *fmt, ...)
-{
- git_buf path = GIT_BUF_INIT,
- contents = GIT_BUF_INIT;
- va_list ap;
- int error;
-
- va_start(ap, fmt);
- git_buf_vprintf(&contents, fmt, ap);
- va_end(ap);
-
- if ((error = git_buf_joinpath(&path, rebase->state_path, filename)) == 0)
- error = git_futils_writebuffer(&contents, path.ptr, flags, REBASE_FILE_MODE);
-
- git_buf_free(&path);
- git_buf_free(&contents);
-
- return error;
-}
-
-static const char *rebase_onto_name(const git_annotated_commit *onto)
-{
- if (onto->ref_name && git__strncmp(onto->ref_name, "refs/heads/", 11) == 0)
- return onto->ref_name + 11;
- else if (onto->ref_name)
- return onto->ref_name;
- else
- return onto->id_str;
-}
-
-static int rebase_setupfiles_merge(git_rebase *rebase)
-{
- git_buf commit_filename = GIT_BUF_INIT;
- char id_str[GIT_OID_HEXSZ];
- git_rebase_operation *operation;
- size_t i;
- int error = 0;
-
- if ((error = rebase_setupfile(rebase, END_FILE, -1, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 ||
- (error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0)
- goto done;
-
- for (i = 0; i < git_array_size(rebase->operations); i++) {
- operation = git_array_get(rebase->operations, i);
-
- git_buf_clear(&commit_filename);
- git_buf_printf(&commit_filename, CMT_FILE_FMT, i+1);
-
- git_oid_fmt(id_str, &operation->id);
-
- if ((error = rebase_setupfile(rebase, commit_filename.ptr, -1,
- "%.*s\n", GIT_OID_HEXSZ, id_str)) < 0)
- goto done;
- }
-
-done:
- git_buf_free(&commit_filename);
- return error;
-}
-
-static int rebase_setupfiles(git_rebase *rebase)
-{
- char onto[GIT_OID_HEXSZ], orig_head[GIT_OID_HEXSZ];
- const char *orig_head_name;
-
- git_oid_fmt(onto, &rebase->onto_id);
- git_oid_fmt(orig_head, &rebase->orig_head_id);
-
- if (p_mkdir(rebase->state_path, REBASE_DIR_MODE) < 0) {
- giterr_set(GITERR_OS, "Failed to create rebase directory '%s'", rebase->state_path);
- return -1;
- }
-
- orig_head_name = rebase->head_detached ? ORIG_DETACHED_HEAD :
- rebase->orig_head_name;
-
- if (git_repository__set_orig_head(rebase->repo, &rebase->orig_head_id) < 0 ||
- rebase_setupfile(rebase, HEAD_NAME_FILE, -1, "%s\n", orig_head_name) < 0 ||
- rebase_setupfile(rebase, ONTO_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 ||
- rebase_setupfile(rebase, ORIG_HEAD_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 ||
- rebase_setupfile(rebase, QUIET_FILE, -1, rebase->quiet ? "t\n" : "\n") < 0)
- return -1;
-
- return rebase_setupfiles_merge(rebase);
-}
-
-int git_rebase_init_options(git_rebase_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_rebase_options, GIT_REBASE_OPTIONS_INIT);
- return 0;
-}
-
-static int rebase_ensure_not_in_progress(git_repository *repo)
-{
- int error;
- git_rebase_type_t type;
-
- if ((error = rebase_state_type(&type, NULL, repo)) < 0)
- return error;
-
- if (type != GIT_REBASE_TYPE_NONE) {
- giterr_set(GITERR_REBASE, "There is an existing rebase in progress");
- return -1;
- }
-
- return 0;
-}
-
-static int rebase_ensure_not_dirty(
- git_repository *repo,
- bool check_index,
- bool check_workdir,
- int fail_with)
-{
- git_tree *head = NULL;
- git_index *index = NULL;
- git_diff *diff = NULL;
- int error = 0;
-
- if (check_index) {
- if ((error = git_repository_head_tree(&head, repo)) < 0 ||
- (error = git_repository_index(&index, repo)) < 0 ||
- (error = git_diff_tree_to_index(&diff, repo, head, index, NULL)) < 0)
- goto done;
-
- if (git_diff_num_deltas(diff) > 0) {
- giterr_set(GITERR_REBASE, "Uncommitted changes exist in index");
- error = fail_with;
- goto done;
- }
-
- git_diff_free(diff);
- diff = NULL;
- }
-
- if (check_workdir) {
- if ((error = git_diff_index_to_workdir(&diff, repo, index, NULL)) < 0)
- goto done;
-
- if (git_diff_num_deltas(diff) > 0) {
- giterr_set(GITERR_REBASE, "Unstaged changes exist in workdir");
- error = fail_with;
- goto done;
- }
- }
-
-done:
- git_diff_free(diff);
- git_index_free(index);
- git_tree_free(head);
-
- return error;
-}
-
-static int rebase_init_operations(
- git_rebase *rebase,
- git_repository *repo,
- const git_annotated_commit *branch,
- const git_annotated_commit *upstream,
- const git_annotated_commit *onto)
-{
- git_revwalk *revwalk = NULL;
- git_commit *commit;
- git_oid id;
- bool merge;
- git_rebase_operation *operation;
- int error;
-
- if (!upstream)
- upstream = onto;
-
- if ((error = git_revwalk_new(&revwalk, rebase->repo)) < 0 ||
- (error = git_revwalk_push(revwalk, git_annotated_commit_id(branch))) < 0 ||
- (error = git_revwalk_hide(revwalk, git_annotated_commit_id(upstream))) < 0)
- goto done;
-
- git_revwalk_sorting(revwalk, GIT_SORT_REVERSE);
-
- while ((error = git_revwalk_next(&id, revwalk)) == 0) {
- if ((error = git_commit_lookup(&commit, repo, &id)) < 0)
- goto done;
-
- merge = (git_commit_parentcount(commit) > 1);
- git_commit_free(commit);
-
- if (merge)
- continue;
-
- operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL);
- GITERR_CHECK_ALLOC(operation);
- }
-
- error = 0;
-
-done:
- git_revwalk_free(revwalk);
- return error;
-}
-
-static int rebase_init_merge(
- git_rebase *rebase,
- git_repository *repo,
- const git_annotated_commit *branch,
- const git_annotated_commit *upstream,
- const git_annotated_commit *onto)
-{
- git_reference *head_ref = NULL;
- git_commit *onto_commit = NULL;
- git_buf reflog = GIT_BUF_INIT;
- git_buf state_path = GIT_BUF_INIT;
- int error;
-
- GIT_UNUSED(upstream);
-
- if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0)
- goto done;
-
- rebase->state_path = git_buf_detach(&state_path);
- GITERR_CHECK_ALLOC(rebase->state_path);
-
- if (branch->ref_name && strcmp(branch->ref_name, "HEAD")) {
- rebase->orig_head_name = git__strdup(branch->ref_name);
- GITERR_CHECK_ALLOC(rebase->orig_head_name);
- } else {
- rebase->head_detached = 1;
- }
-
- rebase->onto_name = git__strdup(rebase_onto_name(onto));
- GITERR_CHECK_ALLOC(rebase->onto_name);
-
- rebase->quiet = rebase->options.quiet;
-
- git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch));
- git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto));
-
- if ((error = rebase_setupfiles(rebase)) < 0 ||
- (error = git_buf_printf(&reflog,
- "rebase: checkout %s", rebase_onto_name(onto))) < 0 ||
- (error = git_commit_lookup(
- &onto_commit, repo, git_annotated_commit_id(onto))) < 0 ||
- (error = git_checkout_tree(repo,
- (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 ||
- (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE,
- git_annotated_commit_id(onto), 1, reflog.ptr)) < 0)
- goto done;
-
-done:
- git_reference_free(head_ref);
- git_commit_free(onto_commit);
- git_buf_free(&reflog);
- git_buf_free(&state_path);
-
- return error;
-}
-
-static int rebase_init_inmemory(
- git_rebase *rebase,
- git_repository *repo,
- const git_annotated_commit *branch,
- const git_annotated_commit *upstream,
- const git_annotated_commit *onto)
-{
- GIT_UNUSED(branch);
- GIT_UNUSED(upstream);
-
- return git_commit_lookup(
- &rebase->last_commit, repo, git_annotated_commit_id(onto));
-}
-
-int git_rebase_init(
- git_rebase **out,
- git_repository *repo,
- const git_annotated_commit *branch,
- const git_annotated_commit *upstream,
- const git_annotated_commit *onto,
- const git_rebase_options *given_opts)
-{
- git_rebase *rebase = NULL;
- git_annotated_commit *head_branch = NULL;
- git_reference *head_ref = NULL;
- bool inmemory = (given_opts && given_opts->inmemory);
- int error;
-
- assert(repo && (upstream || onto));
-
- *out = NULL;
-
- if (!onto)
- onto = upstream;
-
- if ((error = rebase_check_versions(given_opts)) < 0)
- goto done;
-
- if (!inmemory) {
- if ((error = git_repository__ensure_not_bare(repo, "rebase")) < 0 ||
- (error = rebase_ensure_not_in_progress(repo)) < 0 ||
- (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0)
- goto done;
- }
-
- if (!branch) {
- if ((error = git_repository_head(&head_ref, repo)) < 0 ||
- (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0)
- goto done;
-
- branch = head_branch;
- }
-
- if (rebase_alloc(&rebase, given_opts) < 0)
- return -1;
-
- rebase->repo = repo;
- rebase->inmemory = inmemory;
- rebase->type = GIT_REBASE_TYPE_MERGE;
-
- if ((error = rebase_init_operations(rebase, repo, branch, upstream, onto)) < 0)
- goto done;
-
- if (inmemory)
- error = rebase_init_inmemory(rebase, repo, branch, upstream, onto);
- else
- error = rebase_init_merge(rebase, repo, branch ,upstream, onto);
-
- if (error == 0)
- *out = rebase;
-
-done:
- git_reference_free(head_ref);
- git_annotated_commit_free(head_branch);
-
- if (error < 0) {
- rebase_cleanup(rebase);
- git_rebase_free(rebase);
- }
-
- return error;
-}
-
-static void normalize_checkout_options_for_apply(
- git_checkout_options *checkout_opts,
- git_rebase *rebase,
- git_commit *current_commit)
-{
- memcpy(checkout_opts, &rebase->options.checkout_options, sizeof(git_checkout_options));
-
- if (!checkout_opts->ancestor_label)
- checkout_opts->ancestor_label = "ancestor";
-
- if (rebase->type == GIT_REBASE_TYPE_MERGE) {
- if (!checkout_opts->our_label)
- checkout_opts->our_label = rebase->onto_name;
-
- if (!checkout_opts->their_label)
- checkout_opts->their_label = git_commit_summary(current_commit);
- } else {
- abort();
- }
-}
-
-GIT_INLINE(int) rebase_movenext(git_rebase *rebase)
-{
- size_t next = rebase->started ? rebase->current + 1 : 0;
-
- if (next == git_array_size(rebase->operations))
- return GIT_ITEROVER;
-
- rebase->started = 1;
- rebase->current = next;
-
- return 0;
-}
-
-static int rebase_next_merge(
- git_rebase_operation **out,
- git_rebase *rebase)
-{
- git_buf path = GIT_BUF_INIT;
- git_commit *current_commit = NULL, *parent_commit = NULL;
- git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL;
- git_index *index = NULL;
- git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
- git_rebase_operation *operation;
- git_checkout_options checkout_opts;
- char current_idstr[GIT_OID_HEXSZ];
- unsigned int parent_count;
- int error;
-
- *out = NULL;
-
- operation = git_array_get(rebase->operations, rebase->current);
-
- if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 ||
- (error = git_commit_tree(¤t_tree, current_commit)) < 0 ||
- (error = git_repository_head_tree(&head_tree, rebase->repo)) < 0)
- goto done;
-
- if ((parent_count = git_commit_parentcount(current_commit)) > 1) {
- giterr_set(GITERR_REBASE, "Cannot rebase a merge commit");
- error = -1;
- goto done;
- } else if (parent_count) {
- if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 ||
- (error = git_commit_tree(&parent_tree, parent_commit)) < 0)
- goto done;
- }
-
- git_oid_fmt(current_idstr, &operation->id);
-
- normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit);
-
- if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 ||
- (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 ||
- (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 ||
- (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 ||
- (error = git_merge__check_result(rebase->repo, index)) < 0 ||
- (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 ||
- (error = git_indexwriter_commit(&indexwriter)) < 0)
- goto done;
-
- *out = operation;
-
-done:
- git_indexwriter_cleanup(&indexwriter);
- git_index_free(index);
- git_tree_free(current_tree);
- git_tree_free(head_tree);
- git_tree_free(parent_tree);
- git_commit_free(parent_commit);
- git_commit_free(current_commit);
- git_buf_free(&path);
-
- return error;
-}
-
-static int rebase_next_inmemory(
- git_rebase_operation **out,
- git_rebase *rebase)
-{
- git_commit *current_commit = NULL, *parent_commit = NULL;
- git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL;
- git_rebase_operation *operation;
- git_index *index = NULL;
- unsigned int parent_count;
- int error;
-
- *out = NULL;
-
- operation = git_array_get(rebase->operations, rebase->current);
-
- if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 ||
- (error = git_commit_tree(¤t_tree, current_commit)) < 0)
- goto done;
-
- if ((parent_count = git_commit_parentcount(current_commit)) > 1) {
- giterr_set(GITERR_REBASE, "Cannot rebase a merge commit");
- error = -1;
- goto done;
- } else if (parent_count) {
- if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 ||
- (error = git_commit_tree(&parent_tree, parent_commit)) < 0)
- goto done;
- }
-
- if ((error = git_commit_tree(&head_tree, rebase->last_commit)) < 0 ||
- (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0)
- goto done;
-
- if (!rebase->index) {
- rebase->index = index;
- index = NULL;
- } else {
- if ((error = git_index_read_index(rebase->index, index)) < 0)
- goto done;
- }
-
- *out = operation;
-
-done:
- git_commit_free(current_commit);
- git_commit_free(parent_commit);
- git_tree_free(current_tree);
- git_tree_free(head_tree);
- git_tree_free(parent_tree);
- git_index_free(index);
-
- return error;
-}
-
-int git_rebase_next(
- git_rebase_operation **out,
- git_rebase *rebase)
-{
- int error;
-
- assert(out && rebase);
-
- if ((error = rebase_movenext(rebase)) < 0)
- return error;
-
- if (rebase->inmemory)
- error = rebase_next_inmemory(out, rebase);
- else if (rebase->type == GIT_REBASE_TYPE_MERGE)
- error = rebase_next_merge(out, rebase);
- else
- abort();
-
- return error;
-}
-
-int git_rebase_inmemory_index(
- git_index **out,
- git_rebase *rebase)
-{
- assert(out && rebase && rebase->index);
-
- GIT_REFCOUNT_INC(rebase->index);
- *out = rebase->index;
-
- return 0;
-}
-
-static int rebase_commit__create(
- git_commit **out,
- git_rebase *rebase,
- git_index *index,
- git_commit *parent_commit,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message)
-{
- git_rebase_operation *operation;
- git_commit *current_commit = NULL, *commit = NULL;
- git_tree *parent_tree = NULL, *tree = NULL;
- git_oid tree_id, commit_id;
- int error;
-
- operation = git_array_get(rebase->operations, rebase->current);
-
- if (git_index_has_conflicts(index)) {
- giterr_set(GITERR_REBASE, "conflicts have not been resolved");
- error = GIT_EUNMERGED;
- goto done;
- }
-
- if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 ||
- (error = git_commit_tree(&parent_tree, parent_commit)) < 0 ||
- (error = git_index_write_tree_to(&tree_id, index, rebase->repo)) < 0 ||
- (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0)
- goto done;
-
- if (git_oid_equal(&tree_id, git_tree_id(parent_tree))) {
- giterr_set(GITERR_REBASE, "this patch has already been applied");
- error = GIT_EAPPLIED;
- goto done;
- }
-
- if (!author)
- author = git_commit_author(current_commit);
-
- if (!message) {
- message_encoding = git_commit_message_encoding(current_commit);
- message = git_commit_message(current_commit);
- }
-
- if ((error = git_commit_create(&commit_id, rebase->repo, NULL, author,
- committer, message_encoding, message, tree, 1,
- (const git_commit **)&parent_commit)) < 0 ||
- (error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0)
- goto done;
-
- *out = commit;
-
-done:
- if (error < 0)
- git_commit_free(commit);
-
- git_commit_free(current_commit);
- git_tree_free(parent_tree);
- git_tree_free(tree);
-
- return error;
-}
-
-static int rebase_commit_merge(
- git_oid *commit_id,
- git_rebase *rebase,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message)
-{
- git_rebase_operation *operation;
- git_reference *head = NULL;
- git_commit *head_commit = NULL, *commit = NULL;
- git_index *index = NULL;
- char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ];
- int error;
-
- operation = git_array_get(rebase->operations, rebase->current);
- assert(operation);
-
- if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 ||
- (error = git_repository_head(&head, rebase->repo)) < 0 ||
- (error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)) < 0 ||
- (error = git_repository_index(&index, rebase->repo)) < 0 ||
- (error = rebase_commit__create(&commit, rebase, index, head_commit,
- author, committer, message_encoding, message)) < 0 ||
- (error = git_reference__update_for_commit(
- rebase->repo, NULL, "HEAD", git_commit_id(commit), "rebase")) < 0)
- goto done;
-
- git_oid_fmt(old_idstr, &operation->id);
- git_oid_fmt(new_idstr, git_commit_id(commit));
-
- if ((error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND,
- "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr)) < 0)
- goto done;
-
- git_oid_cpy(commit_id, git_commit_id(commit));
-
-done:
- git_index_free(index);
- git_reference_free(head);
- git_commit_free(head_commit);
- git_commit_free(commit);
- return error;
-}
-
-static int rebase_commit_inmemory(
- git_oid *commit_id,
- git_rebase *rebase,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message)
-{
- git_commit *commit = NULL;
- int error = 0;
-
- assert(rebase->index);
- assert(rebase->last_commit);
- assert(rebase->current < rebase->operations.size);
-
- if ((error = rebase_commit__create(&commit, rebase, rebase->index,
- rebase->last_commit, author, committer, message_encoding, message)) < 0)
- goto done;
-
- git_commit_free(rebase->last_commit);
- rebase->last_commit = commit;
-
- git_oid_cpy(commit_id, git_commit_id(commit));
-
-done:
- if (error < 0)
- git_commit_free(commit);
-
- return error;
-}
-
-int git_rebase_commit(
- git_oid *id,
- git_rebase *rebase,
- const git_signature *author,
- const git_signature *committer,
- const char *message_encoding,
- const char *message)
-{
- int error;
-
- assert(rebase && committer);
-
- if (rebase->inmemory)
- error = rebase_commit_inmemory(
- id, rebase, author, committer, message_encoding, message);
- else if (rebase->type == GIT_REBASE_TYPE_MERGE)
- error = rebase_commit_merge(
- id, rebase, author, committer, message_encoding, message);
- else
- abort();
-
- return error;
-}
-
-int git_rebase_abort(git_rebase *rebase)
-{
- git_reference *orig_head_ref = NULL;
- git_commit *orig_head_commit = NULL;
- int error;
-
- assert(rebase);
-
- if (rebase->inmemory)
- return 0;
-
- error = rebase->head_detached ?
- git_reference_create(&orig_head_ref, rebase->repo, GIT_HEAD_FILE,
- &rebase->orig_head_id, 1, "rebase: aborting") :
- git_reference_symbolic_create(
- &orig_head_ref, rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1,
- "rebase: aborting");
-
- if (error < 0)
- goto done;
-
- if ((error = git_commit_lookup(
- &orig_head_commit, rebase->repo, &rebase->orig_head_id)) < 0 ||
- (error = git_reset(rebase->repo, (git_object *)orig_head_commit,
- GIT_RESET_HARD, &rebase->options.checkout_options)) < 0)
- goto done;
-
- error = rebase_cleanup(rebase);
-
-done:
- git_commit_free(orig_head_commit);
- git_reference_free(orig_head_ref);
-
- return error;
-}
-
-static int notes_ref_lookup(git_buf *out, git_rebase *rebase)
-{
- git_config *config = NULL;
- int do_rewrite, error;
-
- if (rebase->options.rewrite_notes_ref) {
- git_buf_attach_notowned(out,
- rebase->options.rewrite_notes_ref,
- strlen(rebase->options.rewrite_notes_ref));
- return 0;
- }
-
- if ((error = git_repository_config(&config, rebase->repo)) < 0 ||
- (error = git_config_get_bool(&do_rewrite, config, "notes.rewrite.rebase")) < 0) {
-
- if (error != GIT_ENOTFOUND)
- goto done;
-
- giterr_clear();
- do_rewrite = 1;
- }
-
- error = do_rewrite ?
- git_config_get_string_buf(out, config, "notes.rewriteref") :
- GIT_ENOTFOUND;
-
-done:
- git_config_free(config);
- return error;
-}
-
-static int rebase_copy_note(
- git_rebase *rebase,
- const char *notes_ref,
- git_oid *from,
- git_oid *to,
- const git_signature *committer)
-{
- git_note *note = NULL;
- git_oid note_id;
- git_signature *who = NULL;
- int error;
-
- if ((error = git_note_read(¬e, rebase->repo, notes_ref, from)) < 0) {
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
-
- goto done;
- }
-
- if (!committer) {
- if((error = git_signature_default(&who, rebase->repo)) < 0) {
- if (error != GIT_ENOTFOUND ||
- (error = git_signature_now(&who, "unknown", "unknown")) < 0)
- goto done;
-
- giterr_clear();
- }
-
- committer = who;
- }
-
- error = git_note_create(¬e_id, rebase->repo, notes_ref,
- git_note_author(note), committer, to, git_note_message(note), 0);
-
-done:
- git_note_free(note);
- git_signature_free(who);
-
- return error;
-}
-
-static int rebase_copy_notes(
- git_rebase *rebase,
- const git_signature *committer)
-{
- git_buf path = GIT_BUF_INIT, rewritten = GIT_BUF_INIT, notes_ref = GIT_BUF_INIT;
- char *pair_list, *fromstr, *tostr, *end;
- git_oid from, to;
- unsigned int linenum = 1;
- int error = 0;
-
- if ((error = notes_ref_lookup(¬es_ref, rebase)) < 0) {
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
-
- goto done;
- }
-
- if ((error = git_buf_joinpath(&path, rebase->state_path, REWRITTEN_FILE)) < 0 ||
- (error = git_futils_readbuffer(&rewritten, path.ptr)) < 0)
- goto done;
-
- pair_list = rewritten.ptr;
-
- while (*pair_list) {
- fromstr = pair_list;
-
- if ((end = strchr(fromstr, '\n')) == NULL)
- goto on_error;
-
- pair_list = end+1;
- *end = '\0';
-
- if ((end = strchr(fromstr, ' ')) == NULL)
- goto on_error;
-
- tostr = end+1;
- *end = '\0';
-
- if (strlen(fromstr) != GIT_OID_HEXSZ ||
- strlen(tostr) != GIT_OID_HEXSZ ||
- git_oid_fromstr(&from, fromstr) < 0 ||
- git_oid_fromstr(&to, tostr) < 0)
- goto on_error;
-
- if ((error = rebase_copy_note(rebase, notes_ref.ptr, &from, &to, committer)) < 0)
- goto done;
-
- linenum++;
- }
-
- goto done;
-
-on_error:
- giterr_set(GITERR_REBASE, "Invalid rewritten file at line %d", linenum);
- error = -1;
-
-done:
- git_buf_free(&rewritten);
- git_buf_free(&path);
- git_buf_free(¬es_ref);
-
- return error;
-}
-
-static int return_to_orig_head(git_rebase *rebase)
-{
- git_reference *terminal_ref = NULL, *branch_ref = NULL, *head_ref = NULL;
- git_commit *terminal_commit = NULL;
- git_buf branch_msg = GIT_BUF_INIT, head_msg = GIT_BUF_INIT;
- char onto[GIT_OID_HEXSZ];
- int error = 0;
-
- git_oid_fmt(onto, &rebase->onto_id);
-
- if ((error = git_buf_printf(&branch_msg,
- "rebase finished: %s onto %.*s",
- rebase->orig_head_name, GIT_OID_HEXSZ, onto)) == 0 &&
- (error = git_buf_printf(&head_msg,
- "rebase finished: returning to %s",
- rebase->orig_head_name)) == 0 &&
- (error = git_repository_head(&terminal_ref, rebase->repo)) == 0 &&
- (error = git_reference_peel((git_object **)&terminal_commit,
- terminal_ref, GIT_OBJ_COMMIT)) == 0 &&
- (error = git_reference_create_matching(&branch_ref,
- rebase->repo, rebase->orig_head_name,
- git_commit_id(terminal_commit), 1,
- &rebase->orig_head_id, branch_msg.ptr)) == 0)
- error = git_reference_symbolic_create(&head_ref,
- rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1,
- head_msg.ptr);
-
- git_buf_free(&head_msg);
- git_buf_free(&branch_msg);
- git_commit_free(terminal_commit);
- git_reference_free(head_ref);
- git_reference_free(branch_ref);
- git_reference_free(terminal_ref);
-
- return error;
-}
-
-int git_rebase_finish(
- git_rebase *rebase,
- const git_signature *signature)
-{
- int error = 0;
-
- assert(rebase);
-
- if (rebase->inmemory)
- return 0;
-
- if (!rebase->head_detached)
- error = return_to_orig_head(rebase);
-
- if (error == 0 && (error = rebase_copy_notes(rebase, signature)) == 0)
- error = rebase_cleanup(rebase);
-
- return error;
-}
-
-size_t git_rebase_operation_entrycount(git_rebase *rebase)
-{
- assert(rebase);
-
- return git_array_size(rebase->operations);
-}
-
-size_t git_rebase_operation_current(git_rebase *rebase)
-{
- assert(rebase);
-
- return rebase->started ? rebase->current : GIT_REBASE_NO_OPERATION;
-}
-
-git_rebase_operation *git_rebase_operation_byindex(git_rebase *rebase, size_t idx)
-{
- assert(rebase);
-
- return git_array_get(rebase->operations, idx);
-}
-
-void git_rebase_free(git_rebase *rebase)
-{
- if (rebase == NULL)
- return;
-
- git_index_free(rebase->index);
- git_commit_free(rebase->last_commit);
- git__free(rebase->onto_name);
- git__free(rebase->orig_head_name);
- git__free(rebase->state_path);
- git_array_clear(rebase->operations);
- git__free((char *)rebase->options.rewrite_notes_ref);
- git__free(rebase);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "posix.h"
-
-#include "git2/object.h"
-#include "git2/refs.h"
-#include "git2/refdb.h"
-#include "git2/sys/refdb_backend.h"
-
-#include "hash.h"
-#include "refdb.h"
-#include "refs.h"
-#include "reflog.h"
-
-int git_refdb_new(git_refdb **out, git_repository *repo)
-{
- git_refdb *db;
-
- assert(out && repo);
-
- db = git__calloc(1, sizeof(*db));
- GITERR_CHECK_ALLOC(db);
-
- db->repo = repo;
-
- *out = db;
- GIT_REFCOUNT_INC(db);
- return 0;
-}
-
-int git_refdb_open(git_refdb **out, git_repository *repo)
-{
- git_refdb *db;
- git_refdb_backend *dir;
-
- assert(out && repo);
-
- *out = NULL;
-
- if (git_refdb_new(&db, repo) < 0)
- return -1;
-
- /* Add the default (filesystem) backend */
- if (git_refdb_backend_fs(&dir, repo) < 0) {
- git_refdb_free(db);
- return -1;
- }
-
- db->repo = repo;
- db->backend = dir;
-
- *out = db;
- return 0;
-}
-
-static void refdb_free_backend(git_refdb *db)
-{
- if (db->backend)
- db->backend->free(db->backend);
-}
-
-int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend)
-{
- refdb_free_backend(db);
- db->backend = backend;
-
- return 0;
-}
-
-int git_refdb_compress(git_refdb *db)
-{
- assert(db);
-
- if (db->backend->compress)
- return db->backend->compress(db->backend);
-
- return 0;
-}
-
-void git_refdb__free(git_refdb *db)
-{
- refdb_free_backend(db);
- git__memzero(db, sizeof(*db));
- git__free(db);
-}
-
-void git_refdb_free(git_refdb *db)
-{
- if (db == NULL)
- return;
-
- GIT_REFCOUNT_DEC(db, git_refdb__free);
-}
-
-int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name)
-{
- assert(exists && refdb && refdb->backend);
-
- return refdb->backend->exists(exists, refdb->backend, ref_name);
-}
-
-int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
-{
- git_reference *ref;
- int error;
-
- assert(db && db->backend && out && ref_name);
-
- error = db->backend->lookup(&ref, db->backend, ref_name);
- if (error < 0)
- return error;
-
- GIT_REFCOUNT_INC(db);
- ref->db = db;
-
- *out = ref;
- return 0;
-}
-
-int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob)
-{
- int error;
-
- if (!db->backend || !db->backend->iterator) {
- giterr_set(GITERR_REFERENCE, "This backend doesn't support iterators");
- return -1;
- }
-
- if ((error = db->backend->iterator(out, db->backend, glob)) < 0)
- return error;
-
- GIT_REFCOUNT_INC(db);
- (*out)->db = db;
-
- return 0;
-}
-
-int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter)
-{
- int error;
-
- if ((error = iter->next(out, iter)) < 0)
- return error;
-
- GIT_REFCOUNT_INC(iter->db);
- (*out)->db = iter->db;
-
- return 0;
-}
-
-int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter)
-{
- return iter->next_name(out, iter);
-}
-
-void git_refdb_iterator_free(git_reference_iterator *iter)
-{
- GIT_REFCOUNT_DEC(iter->db, git_refdb__free);
- iter->free(iter);
-}
-
-int git_refdb_write(git_refdb *db, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target)
-{
- assert(db && db->backend);
-
- GIT_REFCOUNT_INC(db);
- ref->db = db;
-
- return db->backend->write(db->backend, ref, force, who, message, old_id, old_target);
-}
-
-int git_refdb_rename(
- git_reference **out,
- git_refdb *db,
- const char *old_name,
- const char *new_name,
- int force,
- const git_signature *who,
- const char *message)
-{
- int error;
-
- assert(db && db->backend);
- error = db->backend->rename(out, db->backend, old_name, new_name, force, who, message);
- if (error < 0)
- return error;
-
- if (out) {
- GIT_REFCOUNT_INC(db);
- (*out)->db = db;
- }
-
- return 0;
-}
-
-int git_refdb_delete(struct git_refdb *db, const char *ref_name, const git_oid *old_id, const char *old_target)
-{
- assert(db && db->backend);
- return db->backend->del(db->backend, ref_name, old_id, old_target);
-}
-
-int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name)
-{
- int error;
-
- assert(db && db->backend);
-
- if ((error = db->backend->reflog_read(out, db->backend, name)) < 0)
- return error;
-
- GIT_REFCOUNT_INC(db);
- (*out)->db = db;
-
- return 0;
-}
-
-int git_refdb_has_log(git_refdb *db, const char *refname)
-{
- assert(db && refname);
-
- return db->backend->has_log(db->backend, refname);
-}
-
-int git_refdb_ensure_log(git_refdb *db, const char *refname)
-{
- assert(db && refname);
-
- return db->backend->ensure_log(db->backend, refname);
-}
-
-int git_refdb_init_backend(git_refdb_backend *backend, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- backend, version, git_refdb_backend, GIT_REFDB_BACKEND_INIT);
- return 0;
-}
-
-int git_refdb_lock(void **payload, git_refdb *db, const char *refname)
-{
- assert(payload && db && refname);
-
- if (!db->backend->lock) {
- giterr_set(GITERR_REFERENCE, "backend does not support locking");
- return -1;
- }
-
- return db->backend->lock(payload, db->backend, refname);
-}
-
-int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message)
-{
- assert(db);
-
- return db->backend->unlock(db->backend, payload, success, update_reflog, ref, sig, message);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_refdb_h__
-#define INCLUDE_refdb_h__
-
-#include "git2/refdb.h"
-#include "repository.h"
-
-struct git_refdb {
- git_refcount rc;
- git_repository *repo;
- git_refdb_backend *backend;
-};
-
-void git_refdb__free(git_refdb *db);
-
-int git_refdb_exists(
- int *exists,
- git_refdb *refdb,
- const char *ref_name);
-
-int git_refdb_lookup(
- git_reference **out,
- git_refdb *refdb,
- const char *ref_name);
-
-int git_refdb_rename(
- git_reference **out,
- git_refdb *db,
- const char *old_name,
- const char *new_name,
- int force,
- const git_signature *who,
- const char *message);
-
-int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob);
-int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter);
-int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter);
-void git_refdb_iterator_free(git_reference_iterator *iter);
-
-int git_refdb_write(git_refdb *refdb, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target);
-int git_refdb_delete(git_refdb *refdb, const char *ref_name, const git_oid *old_id, const char *old_target);
-
-int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name);
-int git_refdb_reflog_write(git_reflog *reflog);
-
-int git_refdb_has_log(git_refdb *db, const char *refname);
-int git_refdb_ensure_log(git_refdb *refdb, const char *refname);
-
-int git_refdb_lock(void **payload, git_refdb *db, const char *refname);
-int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "refs.h"
-#include "hash.h"
-#include "repository.h"
-#include "fileops.h"
-#include "filebuf.h"
-#include "pack.h"
-#include "reflog.h"
-#include "refdb.h"
-#include "refdb_fs.h"
-#include "iterator.h"
-#include "sortedcache.h"
-#include "signature.h"
-
-#include <git2/tag.h>
-#include <git2/object.h>
-#include <git2/refdb.h>
-#include <git2/branch.h>
-#include <git2/sys/refdb_backend.h>
-#include <git2/sys/refs.h>
-#include <git2/sys/reflog.h>
-
-GIT__USE_STRMAP
-
-#define DEFAULT_NESTING_LEVEL 5
-#define MAX_NESTING_LEVEL 10
-
-enum {
- PACKREF_HAS_PEEL = 1,
- PACKREF_WAS_LOOSE = 2,
- PACKREF_CANNOT_PEEL = 4,
- PACKREF_SHADOWED = 8,
-};
-
-enum {
- PEELING_NONE = 0,
- PEELING_STANDARD,
- PEELING_FULL
-};
-
-struct packref {
- git_oid oid;
- git_oid peel;
- char flags;
- char name[GIT_FLEX_ARRAY];
-};
-
-typedef struct refdb_fs_backend {
- git_refdb_backend parent;
-
- git_repository *repo;
- char *path;
-
- git_sortedcache *refcache;
- int peeling_mode;
- git_iterator_flag_t iterator_flags;
- uint32_t direach_flags;
-} refdb_fs_backend;
-
-static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name);
-
-static int packref_cmp(const void *a_, const void *b_)
-{
- const struct packref *a = a_, *b = b_;
- return strcmp(a->name, b->name);
-}
-
-static int packed_reload(refdb_fs_backend *backend)
-{
- int error;
- git_buf packedrefs = GIT_BUF_INIT;
- char *scan, *eof, *eol;
-
- if (!backend->path)
- return 0;
-
- error = git_sortedcache_lockandload(backend->refcache, &packedrefs);
-
- /*
- * If we can't find the packed-refs, clear table and return.
- * Any other error just gets passed through.
- * If no error, and file wasn't changed, just return.
- * Anything else means we need to refresh the packed refs.
- */
- if (error <= 0) {
- if (error == GIT_ENOTFOUND) {
- git_sortedcache_clear(backend->refcache, true);
- giterr_clear();
- error = 0;
- }
- return error;
- }
-
- /* At this point, refresh the packed refs from the loaded buffer. */
-
- git_sortedcache_clear(backend->refcache, false);
-
- scan = (char *)packedrefs.ptr;
- eof = scan + packedrefs.size;
-
- backend->peeling_mode = PEELING_NONE;
-
- if (*scan == '#') {
- static const char *traits_header = "# pack-refs with: ";
-
- if (git__prefixcmp(scan, traits_header) == 0) {
- scan += strlen(traits_header);
- eol = strchr(scan, '\n');
-
- if (!eol)
- goto parse_failed;
- *eol = '\0';
-
- if (strstr(scan, " fully-peeled ") != NULL) {
- backend->peeling_mode = PEELING_FULL;
- } else if (strstr(scan, " peeled ") != NULL) {
- backend->peeling_mode = PEELING_STANDARD;
- }
-
- scan = eol + 1;
- }
- }
-
- while (scan < eof && *scan == '#') {
- if (!(eol = strchr(scan, '\n')))
- goto parse_failed;
- scan = eol + 1;
- }
-
- while (scan < eof) {
- struct packref *ref;
- git_oid oid;
-
- /* parse "<OID> <refname>\n" */
-
- if (git_oid_fromstr(&oid, scan) < 0)
- goto parse_failed;
- scan += GIT_OID_HEXSZ;
-
- if (*scan++ != ' ')
- goto parse_failed;
- if (!(eol = strchr(scan, '\n')))
- goto parse_failed;
- *eol = '\0';
- if (eol[-1] == '\r')
- eol[-1] = '\0';
-
- if (git_sortedcache_upsert((void **)&ref, backend->refcache, scan) < 0)
- goto parse_failed;
- scan = eol + 1;
-
- git_oid_cpy(&ref->oid, &oid);
-
- /* look for optional "^<OID>\n" */
-
- if (*scan == '^') {
- if (git_oid_fromstr(&oid, scan + 1) < 0)
- goto parse_failed;
- scan += GIT_OID_HEXSZ + 1;
-
- if (scan < eof) {
- if (!(eol = strchr(scan, '\n')))
- goto parse_failed;
- scan = eol + 1;
- }
-
- git_oid_cpy(&ref->peel, &oid);
- ref->flags |= PACKREF_HAS_PEEL;
- }
- else if (backend->peeling_mode == PEELING_FULL ||
- (backend->peeling_mode == PEELING_STANDARD &&
- git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0))
- ref->flags |= PACKREF_CANNOT_PEEL;
- }
-
- git_sortedcache_wunlock(backend->refcache);
- git_buf_free(&packedrefs);
-
- return 0;
-
-parse_failed:
- giterr_set(GITERR_REFERENCE, "Corrupted packed references file");
-
- git_sortedcache_clear(backend->refcache, false);
- git_sortedcache_wunlock(backend->refcache);
- git_buf_free(&packedrefs);
-
- return -1;
-}
-
-static int loose_parse_oid(
- git_oid *oid, const char *filename, git_buf *file_content)
-{
- const char *str = git_buf_cstr(file_content);
-
- if (git_buf_len(file_content) < GIT_OID_HEXSZ)
- goto corrupted;
-
- /* we need to get 40 OID characters from the file */
- if (git_oid_fromstr(oid, str) < 0)
- goto corrupted;
-
- /* If the file is longer than 40 chars, the 41st must be a space */
- str += GIT_OID_HEXSZ;
- if (*str == '\0' || git__isspace(*str))
- return 0;
-
-corrupted:
- giterr_set(GITERR_REFERENCE, "Corrupted loose reference file: %s", filename);
- return -1;
-}
-
-static int loose_readbuffer(git_buf *buf, const char *base, const char *path)
-{
- int error;
-
- /* build full path to file */
- if ((error = git_buf_joinpath(buf, base, path)) < 0 ||
- (error = git_futils_readbuffer(buf, buf->ptr)) < 0)
- git_buf_free(buf);
-
- return error;
-}
-
-static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name)
-{
- int error = 0;
- git_buf ref_file = GIT_BUF_INIT;
- struct packref *ref = NULL;
- git_oid oid;
-
- /* if we fail to load the loose reference, assume someone changed
- * the filesystem under us and skip it...
- */
- if (loose_readbuffer(&ref_file, backend->path, name) < 0) {
- giterr_clear();
- goto done;
- }
-
- /* skip symbolic refs */
- if (!git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF))
- goto done;
-
- /* parse OID from file */
- if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0)
- goto done;
-
- git_sortedcache_wlock(backend->refcache);
-
- if (!(error = git_sortedcache_upsert(
- (void **)&ref, backend->refcache, name))) {
-
- git_oid_cpy(&ref->oid, &oid);
- ref->flags = PACKREF_WAS_LOOSE;
- }
-
- git_sortedcache_wunlock(backend->refcache);
-
-done:
- git_buf_free(&ref_file);
- return error;
-}
-
-static int _dirent_loose_load(void *payload, git_buf *full_path)
-{
- refdb_fs_backend *backend = payload;
- const char *file_path;
-
- if (git__suffixcmp(full_path->ptr, ".lock") == 0)
- return 0;
-
- if (git_path_isdir(full_path->ptr)) {
- int error = git_path_direach(
- full_path, backend->direach_flags, _dirent_loose_load, backend);
- /* Race with the filesystem, ignore it */
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- return 0;
- }
-
- return error;
- }
-
- file_path = full_path->ptr + strlen(backend->path);
-
- return loose_lookup_to_packfile(backend, file_path);
-}
-
-/*
- * Load all the loose references from the repository
- * into the in-memory Packfile, and build a vector with
- * all the references so it can be written back to
- * disk.
- */
-static int packed_loadloose(refdb_fs_backend *backend)
-{
- int error;
- git_buf refs_path = GIT_BUF_INIT;
-
- if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0)
- return -1;
-
- /*
- * Load all the loose files from disk into the Packfile table.
- * This will overwrite any old packed entries with their
- * updated loose versions
- */
- error = git_path_direach(
- &refs_path, backend->direach_flags, _dirent_loose_load, backend);
-
- git_buf_free(&refs_path);
-
- return error;
-}
-
-static int refdb_fs_backend__exists(
- int *exists,
- git_refdb_backend *_backend,
- const char *ref_name)
-{
- refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- git_buf ref_path = GIT_BUF_INIT;
- int error;
-
- assert(backend);
-
- if ((error = packed_reload(backend)) < 0 ||
- (error = git_buf_joinpath(&ref_path, backend->path, ref_name)) < 0)
- return error;
-
- *exists = git_path_isfile(ref_path.ptr) ||
- (git_sortedcache_lookup(backend->refcache, ref_name) != NULL);
-
- git_buf_free(&ref_path);
- return 0;
-}
-
-static const char *loose_parse_symbolic(git_buf *file_content)
-{
- const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);
- const char *refname_start;
-
- refname_start = (const char *)file_content->ptr;
-
- if (git_buf_len(file_content) < header_len + 1) {
- giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
- return NULL;
- }
-
- /*
- * Assume we have already checked for the header
- * before calling this function
- */
- refname_start += header_len;
-
- return refname_start;
-}
-
-static int loose_lookup(
- git_reference **out,
- refdb_fs_backend *backend,
- const char *ref_name)
-{
- git_buf ref_file = GIT_BUF_INIT;
- int error = 0;
-
- if (out)
- *out = NULL;
-
- if ((error = loose_readbuffer(&ref_file, backend->path, ref_name)) < 0)
- /* cannot read loose ref file - gah */;
- else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) {
- const char *target;
-
- git_buf_rtrim(&ref_file);
-
- if (!(target = loose_parse_symbolic(&ref_file)))
- error = -1;
- else if (out != NULL)
- *out = git_reference__alloc_symbolic(ref_name, target);
- } else {
- git_oid oid;
-
- if (!(error = loose_parse_oid(&oid, ref_name, &ref_file)) &&
- out != NULL)
- *out = git_reference__alloc(ref_name, &oid, NULL);
- }
-
- git_buf_free(&ref_file);
- return error;
-}
-
-static int ref_error_notfound(const char *name)
-{
- giterr_set(GITERR_REFERENCE, "Reference '%s' not found", name);
- return GIT_ENOTFOUND;
-}
-
-static int packed_lookup(
- git_reference **out,
- refdb_fs_backend *backend,
- const char *ref_name)
-{
- int error = 0;
- struct packref *entry;
-
- if ((error = packed_reload(backend)) < 0)
- return error;
-
- if (git_sortedcache_rlock(backend->refcache) < 0)
- return -1;
-
- entry = git_sortedcache_lookup(backend->refcache, ref_name);
- if (!entry) {
- error = ref_error_notfound(ref_name);
- } else {
- *out = git_reference__alloc(ref_name, &entry->oid, &entry->peel);
- if (!*out)
- error = -1;
- }
-
- git_sortedcache_runlock(backend->refcache);
-
- return error;
-}
-
-static int refdb_fs_backend__lookup(
- git_reference **out,
- git_refdb_backend *_backend,
- const char *ref_name)
-{
- refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- int error;
-
- assert(backend);
-
- if (!(error = loose_lookup(out, backend, ref_name)))
- return 0;
-
- /* only try to lookup this reference on the packfile if it
- * wasn't found on the loose refs; not if there was a critical error */
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = packed_lookup(out, backend, ref_name);
- }
-
- return error;
-}
-
-typedef struct {
- git_reference_iterator parent;
-
- char *glob;
-
- git_pool pool;
- git_vector loose;
-
- git_sortedcache *cache;
- size_t loose_pos;
- size_t packed_pos;
-} refdb_fs_iter;
-
-static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
-{
- refdb_fs_iter *iter = (refdb_fs_iter *) _iter;
-
- git_vector_free(&iter->loose);
- git_pool_clear(&iter->pool);
- git_sortedcache_free(iter->cache);
- git__free(iter);
-}
-
-static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
-{
- int error = 0;
- git_buf path = GIT_BUF_INIT;
- git_iterator *fsit = NULL;
- git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
- const git_index_entry *entry = NULL;
-
- if (!backend->path) /* do nothing if no path for loose refs */
- return 0;
-
- fsit_opts.flags = backend->iterator_flags;
-
- if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 ||
- (error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) {
- git_buf_free(&path);
- return error;
- }
-
- error = git_buf_sets(&path, GIT_REFS_DIR);
-
- while (!error && !git_iterator_advance(&entry, fsit)) {
- const char *ref_name;
- struct packref *ref;
- char *ref_dup;
-
- git_buf_truncate(&path, strlen(GIT_REFS_DIR));
- git_buf_puts(&path, entry->path);
- ref_name = git_buf_cstr(&path);
-
- if (git__suffixcmp(ref_name, ".lock") == 0 ||
- (iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0))
- continue;
-
- git_sortedcache_rlock(backend->refcache);
- ref = git_sortedcache_lookup(backend->refcache, ref_name);
- if (ref)
- ref->flags |= PACKREF_SHADOWED;
- git_sortedcache_runlock(backend->refcache);
-
- ref_dup = git_pool_strdup(&iter->pool, ref_name);
- if (!ref_dup)
- error = -1;
- else
- error = git_vector_insert(&iter->loose, ref_dup);
- }
-
- git_iterator_free(fsit);
- git_buf_free(&path);
-
- return error;
-}
-
-static int refdb_fs_backend__iterator_next(
- git_reference **out, git_reference_iterator *_iter)
-{
- int error = GIT_ITEROVER;
- refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
- refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
- struct packref *ref;
-
- while (iter->loose_pos < iter->loose.length) {
- const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
-
- if (loose_lookup(out, backend, path) == 0)
- return 0;
-
- giterr_clear();
- }
-
- if (!iter->cache) {
- if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
- return error;
- }
-
- error = GIT_ITEROVER;
- while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
- ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
- if (!ref) /* stop now if another thread deleted refs and we past end */
- break;
-
- if (ref->flags & PACKREF_SHADOWED)
- continue;
- if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
- continue;
-
- *out = git_reference__alloc(ref->name, &ref->oid, &ref->peel);
- error = (*out != NULL) ? 0 : -1;
- break;
- }
-
- return error;
-}
-
-static int refdb_fs_backend__iterator_next_name(
- const char **out, git_reference_iterator *_iter)
-{
- int error = GIT_ITEROVER;
- refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
- refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
- struct packref *ref;
-
- while (iter->loose_pos < iter->loose.length) {
- const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
-
- if (loose_lookup(NULL, backend, path) == 0) {
- *out = path;
- return 0;
- }
-
- giterr_clear();
- }
-
- if (!iter->cache) {
- if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
- return error;
- }
-
- error = GIT_ITEROVER;
- while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
- ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
- if (!ref) /* stop now if another thread deleted refs and we past end */
- break;
-
- if (ref->flags & PACKREF_SHADOWED)
- continue;
- if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
- continue;
-
- *out = ref->name;
- error = 0;
- break;
- }
-
- return error;
-}
-
-static int refdb_fs_backend__iterator(
- git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
-{
- int error;
- refdb_fs_iter *iter;
- refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
-
- assert(backend);
-
- if ((error = packed_reload(backend)) < 0)
- return error;
-
- iter = git__calloc(1, sizeof(refdb_fs_iter));
- GITERR_CHECK_ALLOC(iter);
-
- git_pool_init(&iter->pool, 1);
-
- if (git_vector_init(&iter->loose, 8, NULL) < 0)
- goto fail;
-
- if (glob != NULL &&
- (iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL)
- goto fail;
-
- iter->parent.next = refdb_fs_backend__iterator_next;
- iter->parent.next_name = refdb_fs_backend__iterator_next_name;
- iter->parent.free = refdb_fs_backend__iterator_free;
-
- if (iter_load_loose_paths(backend, iter) < 0)
- goto fail;
-
- *out = (git_reference_iterator *)iter;
- return 0;
-
-fail:
- refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
- return -1;
-}
-
-static bool ref_is_available(
- const char *old_ref, const char *new_ref, const char *this_ref)
-{
- if (old_ref == NULL || strcmp(old_ref, this_ref)) {
- size_t reflen = strlen(this_ref);
- size_t newlen = strlen(new_ref);
- size_t cmplen = reflen < newlen ? reflen : newlen;
- const char *lead = reflen < newlen ? new_ref : this_ref;
-
- if (!strncmp(new_ref, this_ref, cmplen) && lead[cmplen] == '/') {
- return false;
- }
- }
-
- return true;
-}
-
-static int reference_path_available(
- refdb_fs_backend *backend,
- const char *new_ref,
- const char* old_ref,
- int force)
-{
- size_t i;
- int error;
-
- if ((error = packed_reload(backend)) < 0)
- return error;
-
- if (!force) {
- int exists;
-
- if ((error = refdb_fs_backend__exists(
- &exists, (git_refdb_backend *)backend, new_ref)) < 0) {
- return error;
- }
-
- if (exists) {
- giterr_set(GITERR_REFERENCE,
- "Failed to write reference '%s': a reference with "
- "that name already exists.", new_ref);
- return GIT_EEXISTS;
- }
- }
-
- git_sortedcache_rlock(backend->refcache);
-
- for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
- struct packref *ref = git_sortedcache_entry(backend->refcache, i);
-
- if (ref && !ref_is_available(old_ref, new_ref, ref->name)) {
- git_sortedcache_runlock(backend->refcache);
- giterr_set(GITERR_REFERENCE,
- "Path to reference '%s' collides with existing one", new_ref);
- return -1;
- }
- }
-
- git_sortedcache_runlock(backend->refcache);
- return 0;
-}
-
-static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name)
-{
- int error;
- git_buf ref_path = GIT_BUF_INIT;
-
- assert(file && backend && name);
-
- if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
- giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name);
- return GIT_EINVALIDSPEC;
- }
-
- /* Remove a possibly existing empty directory hierarchy
- * which name would collide with the reference name
- */
- if ((error = git_futils_rmdir_r(name, backend->path, GIT_RMDIR_SKIP_NONEMPTY)) < 0)
- return error;
-
- if (git_buf_joinpath(&ref_path, backend->path, name) < 0)
- return -1;
-
- error = git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE);
-
- if (error == GIT_EDIRECTORY)
- giterr_set(GITERR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name);
-
- git_buf_free(&ref_path);
- return error;
-}
-
-static int loose_commit(git_filebuf *file, const git_reference *ref)
-{
- assert(file && ref);
-
- if (ref->type == GIT_REF_OID) {
- char oid[GIT_OID_HEXSZ + 1];
- git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
-
- git_filebuf_printf(file, "%s\n", oid);
- } else if (ref->type == GIT_REF_SYMBOLIC) {
- git_filebuf_printf(file, GIT_SYMREF "%s\n", ref->target.symbolic);
- } else {
- assert(0); /* don't let this happen */
- }
-
- return git_filebuf_commit(file);
-}
-
-static int refdb_fs_backend__lock(void **out, git_refdb_backend *_backend, const char *refname)
-{
- int error;
- git_filebuf *lock;
- refdb_fs_backend *backend = (refdb_fs_backend *) _backend;
-
- lock = git__calloc(1, sizeof(git_filebuf));
- GITERR_CHECK_ALLOC(lock);
-
- if ((error = loose_lock(lock, backend, refname)) < 0) {
- git__free(lock);
- return error;
- }
-
- *out = lock;
- return 0;
-}
-
-static int refdb_fs_backend__write_tail(
- git_refdb_backend *_backend,
- const git_reference *ref,
- git_filebuf *file,
- int update_reflog,
- const git_signature *who,
- const char *message,
- const git_oid *old_id,
- const char *old_target);
-
-static int refdb_fs_backend__delete_tail(
- git_refdb_backend *_backend,
- git_filebuf *file,
- const char *ref_name,
- const git_oid *old_id, const char *old_target);
-
-static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog,
- const git_reference *ref, const git_signature *sig, const char *message)
-{
- git_filebuf *lock = (git_filebuf *) payload;
- int error = 0;
-
- if (success == 2)
- error = refdb_fs_backend__delete_tail(backend, lock, ref->name, NULL, NULL);
- else if (success)
- error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, sig, message, NULL, NULL);
- else
- git_filebuf_cleanup(lock);
-
- git__free(lock);
- return error;
-}
-
-/*
- * Find out what object this reference resolves to.
- *
- * For references that point to a 'big' tag (e.g. an
- * actual tag object on the repository), we need to
- * cache on the packfile the OID of the object to
- * which that 'big tag' is pointing to.
- */
-static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
-{
- git_object *object;
-
- if (ref->flags & PACKREF_HAS_PEEL || ref->flags & PACKREF_CANNOT_PEEL)
- return 0;
-
- /*
- * Find the tagged object in the repository
- */
- if (git_object_lookup(&object, backend->repo, &ref->oid, GIT_OBJ_ANY) < 0)
- return -1;
-
- /*
- * If the tagged object is a Tag object, we need to resolve it;
- * if the ref is actually a 'weak' ref, we don't need to resolve
- * anything.
- */
- if (git_object_type(object) == GIT_OBJ_TAG) {
- git_tag *tag = (git_tag *)object;
-
- /*
- * Find the object pointed at by this tag
- */
- git_oid_cpy(&ref->peel, git_tag_target_id(tag));
- ref->flags |= PACKREF_HAS_PEEL;
-
- /*
- * The reference has now cached the resolved OID, and is
- * marked at such. When written to the packfile, it'll be
- * accompanied by this resolved oid
- */
- }
-
- git_object_free(object);
- return 0;
-}
-
-/*
- * Write a single reference into a packfile
- */
-static int packed_write_ref(struct packref *ref, git_filebuf *file)
-{
- char oid[GIT_OID_HEXSZ + 1];
- git_oid_nfmt(oid, sizeof(oid), &ref->oid);
-
- /*
- * For references that peel to an object in the repo, we must
- * write the resulting peel on a separate line, e.g.
- *
- * 6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
- * ^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
- *
- * This obviously only applies to tags.
- * The required peels have already been loaded into `ref->peel_target`.
- */
- if (ref->flags & PACKREF_HAS_PEEL) {
- char peel[GIT_OID_HEXSZ + 1];
- git_oid_nfmt(peel, sizeof(peel), &ref->peel);
-
- if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
- return -1;
- } else {
- if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Remove all loose references
- *
- * Once we have successfully written a packfile,
- * all the loose references that were packed must be
- * removed from disk.
- *
- * This is a dangerous method; make sure the packfile
- * is well-written, because we are destructing references
- * here otherwise.
- */
-static int packed_remove_loose(refdb_fs_backend *backend)
-{
- size_t i;
- git_filebuf lock = GIT_FILEBUF_INIT;
- git_buf ref_content = GIT_BUF_INIT;
- int error = 0;
-
- /* backend->refcache is already locked when this is called */
-
- for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
- struct packref *ref = git_sortedcache_entry(backend->refcache, i);
- git_oid current_id;
-
- if (!ref || !(ref->flags & PACKREF_WAS_LOOSE))
- continue;
-
- git_filebuf_cleanup(&lock);
-
- /* We need to stop anybody from updating the ref while we try to do a safe delete */
- error = loose_lock(&lock, backend, ref->name);
- /* If someone else is updating it, let them do it */
- if (error == GIT_EEXISTS || error == GIT_ENOTFOUND)
- continue;
-
- if (error < 0) {
- git_buf_free(&ref_content);
- giterr_set(GITERR_REFERENCE, "failed to lock loose reference '%s'", ref->name);
- return error;
- }
-
- error = git_futils_readbuffer(&ref_content, lock.path_original);
- /* Someone else beat us to cleaning up the ref, let's simply continue */
- if (error == GIT_ENOTFOUND)
- continue;
-
- /* This became a symref between us packing and trying to delete it, so ignore it */
- if (!git__prefixcmp(ref_content.ptr, GIT_SYMREF))
- continue;
-
- /* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */
- if (loose_parse_oid(¤t_id, lock.path_original, &ref_content) < 0)
- continue;
-
- /* If the ref moved since we packed it, we must not delete it */
- if (!git_oid_equal(¤t_id, &ref->oid))
- continue;
-
- /*
- * if we fail to remove a single file, this is *not* good,
- * but we should keep going and remove as many as possible.
- * If we fail to remove, the ref is still in the old state, so
- * we haven't lost information.
- */
- p_unlink(lock.path_original);
- }
-
- git_buf_free(&ref_content);
- git_filebuf_cleanup(&lock);
- return 0;
-}
-
-/*
- * Write all the contents in the in-memory packfile to disk.
- */
-static int packed_write(refdb_fs_backend *backend)
-{
- git_sortedcache *refcache = backend->refcache;
- git_filebuf pack_file = GIT_FILEBUF_INIT;
- int error;
- size_t i;
-
- /* lock the cache to updates while we do this */
- if ((error = git_sortedcache_wlock(refcache)) < 0)
- return error;
-
- /* Open the file! */
- if ((error = git_filebuf_open(&pack_file, git_sortedcache_path(refcache), 0, GIT_PACKEDREFS_FILE_MODE)) < 0)
- goto fail;
-
- /* Packfiles have a header... apparently
- * This is in fact not required, but we might as well print it
- * just for kicks */
- if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < 0)
- goto fail;
-
- for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
- struct packref *ref = git_sortedcache_entry(refcache, i);
- assert(ref);
-
- if ((error = packed_find_peel(backend, ref)) < 0)
- goto fail;
-
- if ((error = packed_write_ref(ref, &pack_file)) < 0)
- goto fail;
- }
-
- /* if we've written all the references properly, we can commit
- * the packfile to make the changes effective */
- if ((error = git_filebuf_commit(&pack_file)) < 0)
- goto fail;
-
- /* when and only when the packfile has been properly written,
- * we can go ahead and remove the loose refs */
- if ((error = packed_remove_loose(backend)) < 0)
- goto fail;
-
- git_sortedcache_updated(refcache);
- git_sortedcache_wunlock(refcache);
-
- /* we're good now */
- return 0;
-
-fail:
- git_filebuf_cleanup(&pack_file);
- git_sortedcache_wunlock(refcache);
-
- return error;
-}
-
-static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message);
-static int has_reflog(git_repository *repo, const char *name);
-
-/* We only write if it's under heads/, remotes/ or notes/ or if it already has a log */
-static int should_write_reflog(int *write, git_repository *repo, const char *name)
-{
- int error, logall;
-
- error = git_repository__cvar(&logall, repo, GIT_CVAR_LOGALLREFUPDATES);
- if (error < 0)
- return error;
-
- /* Defaults to the opposite of the repo being bare */
- if (logall == GIT_LOGALLREFUPDATES_UNSET)
- logall = !git_repository_is_bare(repo);
-
- if (!logall) {
- *write = 0;
- } else if (has_reflog(repo, name)) {
- *write = 1;
- } else if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR) ||
- !git__strcmp(name, GIT_HEAD_FILE) ||
- !git__prefixcmp(name, GIT_REFS_REMOTES_DIR) ||
- !git__prefixcmp(name, GIT_REFS_NOTES_DIR)) {
- *write = 1;
- } else {
- *write = 0;
- }
-
- return 0;
-}
-
-static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name,
- const git_oid *old_id, const char *old_target)
-{
- int error = 0;
- git_reference *old_ref = NULL;
-
- *cmp = 0;
- /* It "matches" if there is no old value to compare against */
- if (!old_id && !old_target)
- return 0;
-
- if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0)
- goto out;
-
- /* If the types don't match, there's no way the values do */
- if (old_id && old_ref->type != GIT_REF_OID) {
- *cmp = -1;
- goto out;
- }
- if (old_target && old_ref->type != GIT_REF_SYMBOLIC) {
- *cmp = 1;
- goto out;
- }
-
- if (old_id && old_ref->type == GIT_REF_OID)
- *cmp = git_oid_cmp(old_id, &old_ref->target.oid);
-
- if (old_target && old_ref->type == GIT_REF_SYMBOLIC)
- *cmp = git__strcmp(old_target, old_ref->target.symbolic);
-
-out:
- git_reference_free(old_ref);
-
- return error;
-}
-
-/*
- * The git.git comment regarding this, for your viewing pleasure:
- *
- * Special hack: If a branch is updated directly and HEAD
- * points to it (may happen on the remote side of a push
- * for example) then logically the HEAD reflog should be
- * updated too.
- * A generic solution implies reverse symref information,
- * but finding all symrefs pointing to the given branch
- * would be rather costly for this rare event (the direct
- * update of a branch) to be worth it. So let's cheat and
- * check with HEAD only which should cover 99% of all usage
- * scenarios (even 100% of the default ones).
- */
-static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message)
-{
- int error;
- git_oid old_id = {{0}};
- git_reference *tmp = NULL, *head = NULL, *peeled = NULL;
- const char *name;
-
- if (ref->type == GIT_REF_SYMBOLIC)
- return 0;
-
- /* if we can't resolve, we use {0}*40 as old id */
- git_reference_name_to_id(&old_id, backend->repo, ref->name);
-
- if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0)
- return error;
-
- if (git_reference_type(head) == GIT_REF_OID)
- goto cleanup;
-
- if ((error = git_reference_lookup(&tmp, backend->repo, GIT_HEAD_FILE)) < 0)
- goto cleanup;
-
- /* Go down the symref chain until we find the branch */
- while (git_reference_type(tmp) == GIT_REF_SYMBOLIC) {
- error = git_reference_lookup(&peeled, backend->repo, git_reference_symbolic_target(tmp));
- if (error < 0)
- break;
-
- git_reference_free(tmp);
- tmp = peeled;
- }
-
- if (error == GIT_ENOTFOUND) {
- error = 0;
- name = git_reference_symbolic_target(tmp);
- } else if (error < 0) {
- goto cleanup;
- } else {
- name = git_reference_name(tmp);
- }
-
- if (strcmp(name, ref->name))
- goto cleanup;
-
- error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message);
-
-cleanup:
- git_reference_free(tmp);
- git_reference_free(head);
- return error;
-}
-
-static int refdb_fs_backend__write(
- git_refdb_backend *_backend,
- const git_reference *ref,
- int force,
- const git_signature *who,
- const char *message,
- const git_oid *old_id,
- const char *old_target)
-{
- refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- git_filebuf file = GIT_FILEBUF_INIT;
- int error = 0;
-
- assert(backend);
-
- if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0)
- return error;
-
- /* We need to perform the reflog append and old value check under the ref's lock */
- if ((error = loose_lock(&file, backend, ref->name)) < 0)
- return error;
-
- return refdb_fs_backend__write_tail(_backend, ref, &file, true, who, message, old_id, old_target);
-}
-
-static int refdb_fs_backend__write_tail(
- git_refdb_backend *_backend,
- const git_reference *ref,
- git_filebuf *file,
- int update_reflog,
- const git_signature *who,
- const char *message,
- const git_oid *old_id,
- const char *old_target)
-{
- refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- int error = 0, cmp = 0, should_write;
- const char *new_target = NULL;
- const git_oid *new_id = NULL;
-
- if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0)
- goto on_error;
-
- if (cmp) {
- giterr_set(GITERR_REFERENCE, "old reference value does not match");
- error = GIT_EMODIFIED;
- goto on_error;
- }
-
- if (ref->type == GIT_REF_SYMBOLIC)
- new_target = ref->target.symbolic;
- else
- new_id = &ref->target.oid;
-
- error = cmp_old_ref(&cmp, _backend, ref->name, new_id, new_target);
- if (error < 0 && error != GIT_ENOTFOUND)
- goto on_error;
-
- /* Don't update if we have the same value */
- if (!error && !cmp) {
- error = 0;
- goto on_error; /* not really error */
- }
-
- if (update_reflog) {
- if ((error = should_write_reflog(&should_write, backend->repo, ref->name)) < 0)
- goto on_error;
-
- if (should_write) {
- if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0)
- goto on_error;
- if ((error = maybe_append_head(backend, ref, who, message)) < 0)
- goto on_error;
- }
- }
-
- return loose_commit(file, ref);
-
-on_error:
- git_filebuf_cleanup(file);
- return error;
-}
-
-static int refdb_fs_backend__delete(
- git_refdb_backend *_backend,
- const char *ref_name,
- const git_oid *old_id, const char *old_target)
-{
- refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- git_filebuf file = GIT_FILEBUF_INIT;
- int error = 0;
-
- assert(backend && ref_name);
-
- if ((error = loose_lock(&file, backend, ref_name)) < 0)
- return error;
-
- if ((error = refdb_reflog_fs__delete(_backend, ref_name)) < 0) {
- git_filebuf_cleanup(&file);
- return error;
- }
-
- return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target);
-}
-
-static int refdb_fs_backend__delete_tail(
- git_refdb_backend *_backend,
- git_filebuf *file,
- const char *ref_name,
- const git_oid *old_id, const char *old_target)
-{
- refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- git_buf loose_path = GIT_BUF_INIT;
- size_t pack_pos;
- int error = 0, cmp = 0;
- bool loose_deleted = 0;
-
- error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target);
- if (error < 0)
- goto cleanup;
-
- if (cmp) {
- giterr_set(GITERR_REFERENCE, "old reference value does not match");
- error = GIT_EMODIFIED;
- goto cleanup;
- }
-
- /* If a loose reference exists, remove it from the filesystem */
- if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0)
- return -1;
-
-
- error = p_unlink(loose_path.ptr);
- if (error < 0 && errno == ENOENT)
- error = 0;
- else if (error < 0)
- goto cleanup;
- else if (error == 0)
- loose_deleted = 1;
-
- if ((error = packed_reload(backend)) < 0)
- goto cleanup;
-
- /* If a packed reference exists, remove it from the packfile and repack */
- if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
- goto cleanup;
-
- if (!(error = git_sortedcache_lookup_index(
- &pack_pos, backend->refcache, ref_name)))
- error = git_sortedcache_remove(backend->refcache, pack_pos);
-
- git_sortedcache_wunlock(backend->refcache);
-
- if (error == GIT_ENOTFOUND) {
- error = loose_deleted ? 0 : ref_error_notfound(ref_name);
- goto cleanup;
- }
-
- error = packed_write(backend);
-
-cleanup:
- git_buf_free(&loose_path);
- git_filebuf_cleanup(file);
-
- return error;
-}
-
-static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name);
-
-static int refdb_fs_backend__rename(
- git_reference **out,
- git_refdb_backend *_backend,
- const char *old_name,
- const char *new_name,
- int force,
- const git_signature *who,
- const char *message)
-{
- refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- git_reference *old, *new;
- git_filebuf file = GIT_FILEBUF_INIT;
- int error;
-
- assert(backend);
-
- if ((error = reference_path_available(
- backend, new_name, old_name, force)) < 0 ||
- (error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
- return error;
-
- if ((error = refdb_fs_backend__delete(_backend, old_name, NULL, NULL)) < 0) {
- git_reference_free(old);
- return error;
- }
-
- new = git_reference__set_name(old, new_name);
- if (!new) {
- git_reference_free(old);
- return -1;
- }
-
- if ((error = loose_lock(&file, backend, new->name)) < 0) {
- git_reference_free(new);
- return error;
- }
-
- /* Try to rename the refog; it's ok if the old doesn't exist */
- error = refdb_reflog_fs__rename(_backend, old_name, new_name);
- if (((error == 0) || (error == GIT_ENOTFOUND)) &&
- ((error = reflog_append(backend, new, git_reference_target(new), NULL, who, message)) < 0)) {
- git_reference_free(new);
- git_filebuf_cleanup(&file);
- return error;
- }
-
- if (error < 0) {
- git_reference_free(new);
- git_filebuf_cleanup(&file);
- return error;
- }
-
-
- if ((error = loose_commit(&file, new)) < 0 || out == NULL) {
- git_reference_free(new);
- return error;
- }
-
- *out = new;
- return 0;
-}
-
-static int refdb_fs_backend__compress(git_refdb_backend *_backend)
-{
- int error;
- refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
-
- assert(backend);
-
- if ((error = packed_reload(backend)) < 0 || /* load the existing packfile */
- (error = packed_loadloose(backend)) < 0 || /* add all the loose refs */
- (error = packed_write(backend)) < 0) /* write back to disk */
- return error;
-
- return 0;
-}
-
-static void refdb_fs_backend__free(git_refdb_backend *_backend)
-{
- refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
-
- assert(backend);
-
- git_sortedcache_free(backend->refcache);
- git__free(backend->path);
- git__free(backend);
-}
-
-static int setup_namespace(git_buf *path, git_repository *repo)
-{
- char *parts, *start, *end;
-
- /* Not all repositories have a path */
- if (repo->path_repository == NULL)
- return 0;
-
- /* Load the path to the repo first */
- git_buf_puts(path, repo->path_repository);
-
- /* if the repo is not namespaced, nothing else to do */
- if (repo->namespace == NULL)
- return 0;
-
- parts = end = git__strdup(repo->namespace);
- if (parts == NULL)
- return -1;
-
- /*
- * From `man gitnamespaces`:
- * namespaces which include a / will expand to a hierarchy
- * of namespaces; for example, GIT_NAMESPACE=foo/bar will store
- * refs under refs/namespaces/foo/refs/namespaces/bar/
- */
- while ((start = git__strsep(&end, "/")) != NULL) {
- git_buf_printf(path, "refs/namespaces/%s/", start);
- }
-
- git_buf_printf(path, "refs/namespaces/%s/refs", end);
- git__free(parts);
-
- /* Make sure that the folder with the namespace exists */
- if (git_futils_mkdir_relative(git_buf_cstr(path), repo->path_repository,
- 0777, GIT_MKDIR_PATH, NULL) < 0)
- return -1;
-
- /* Return root of the namespaced path, i.e. without the trailing '/refs' */
- git_buf_rtruncate_at_char(path, '/');
- return 0;
-}
-
-static int reflog_alloc(git_reflog **reflog, const char *name)
-{
- git_reflog *log;
-
- *reflog = NULL;
-
- log = git__calloc(1, sizeof(git_reflog));
- GITERR_CHECK_ALLOC(log);
-
- log->ref_name = git__strdup(name);
- GITERR_CHECK_ALLOC(log->ref_name);
-
- if (git_vector_init(&log->entries, 0, NULL) < 0) {
- git__free(log->ref_name);
- git__free(log);
- return -1;
- }
-
- *reflog = log;
-
- return 0;
-}
-
-static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
-{
- const char *ptr;
- git_reflog_entry *entry;
-
-#define seek_forward(_increase) do { \
- if (_increase >= buf_size) { \
- giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
- goto fail; \
- } \
- buf += _increase; \
- buf_size -= _increase; \
- } while (0)
-
- while (buf_size > GIT_REFLOG_SIZE_MIN) {
- entry = git__calloc(1, sizeof(git_reflog_entry));
- GITERR_CHECK_ALLOC(entry);
-
- entry->committer = git__calloc(1, sizeof(git_signature));
- GITERR_CHECK_ALLOC(entry->committer);
-
- if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
- goto fail;
- seek_forward(GIT_OID_HEXSZ + 1);
-
- if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
- goto fail;
- seek_forward(GIT_OID_HEXSZ + 1);
-
- ptr = buf;
-
- /* Seek forward to the end of the signature. */
- while (*buf && *buf != '\t' && *buf != '\n')
- seek_forward(1);
-
- if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
- goto fail;
-
- if (*buf == '\t') {
- /* We got a message. Read everything till we reach LF. */
- seek_forward(1);
- ptr = buf;
-
- while (*buf && *buf != '\n')
- seek_forward(1);
-
- entry->msg = git__strndup(ptr, buf - ptr);
- GITERR_CHECK_ALLOC(entry->msg);
- } else
- entry->msg = NULL;
-
- while (*buf && *buf == '\n' && buf_size > 1)
- seek_forward(1);
-
- if (git_vector_insert(&log->entries, entry) < 0)
- goto fail;
- }
-
- return 0;
-
-#undef seek_forward
-
-fail:
- git_reflog_entry__free(entry);
-
- return -1;
-}
-
-static int create_new_reflog_file(const char *filepath)
-{
- int fd, error;
-
- if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
- return error;
-
- if ((fd = p_open(filepath,
- O_WRONLY | O_CREAT,
- GIT_REFLOG_FILE_MODE)) < 0)
- return -1;
-
- return p_close(fd);
-}
-
-GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name)
-{
- return git_buf_join3(path, '/', repo->path_repository, GIT_REFLOG_DIR, name);
-}
-
-static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name)
-{
- refdb_fs_backend *backend;
- git_repository *repo;
- git_buf path = GIT_BUF_INIT;
- int error;
-
- assert(_backend && name);
-
- backend = (refdb_fs_backend *) _backend;
- repo = backend->repo;
-
- if ((error = retrieve_reflog_path(&path, repo, name)) < 0)
- return error;
-
- error = create_new_reflog_file(git_buf_cstr(&path));
- git_buf_free(&path);
-
- return error;
-}
-
-static int has_reflog(git_repository *repo, const char *name)
-{
- int ret = 0;
- git_buf path = GIT_BUF_INIT;
-
- if (retrieve_reflog_path(&path, repo, name) < 0)
- goto cleanup;
-
- ret = git_path_isfile(git_buf_cstr(&path));
-
-cleanup:
- git_buf_free(&path);
- return ret;
-}
-
-static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *name)
-{
- refdb_fs_backend *backend;
-
- assert(_backend && name);
-
- backend = (refdb_fs_backend *) _backend;
-
- return has_reflog(backend->repo, name);
-}
-
-static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
-{
- int error = -1;
- git_buf log_path = GIT_BUF_INIT;
- git_buf log_file = GIT_BUF_INIT;
- git_reflog *log = NULL;
- git_repository *repo;
- refdb_fs_backend *backend;
-
- assert(out && _backend && name);
-
- backend = (refdb_fs_backend *) _backend;
- repo = backend->repo;
-
- if (reflog_alloc(&log, name) < 0)
- return -1;
-
- if (retrieve_reflog_path(&log_path, repo, name) < 0)
- goto cleanup;
-
- error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- if ((error == GIT_ENOTFOUND) &&
- ((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
- goto cleanup;
-
- if ((error = reflog_parse(log,
- git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
- goto cleanup;
-
- *out = log;
- goto success;
-
-cleanup:
- git_reflog_free(log);
-
-success:
- git_buf_free(&log_file);
- git_buf_free(&log_path);
-
- return error;
-}
-
-static int serialize_reflog_entry(
- git_buf *buf,
- const git_oid *oid_old,
- const git_oid *oid_new,
- const git_signature *committer,
- const char *msg)
-{
- char raw_old[GIT_OID_HEXSZ+1];
- char raw_new[GIT_OID_HEXSZ+1];
-
- git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
- git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
-
- git_buf_clear(buf);
-
- git_buf_puts(buf, raw_old);
- git_buf_putc(buf, ' ');
- git_buf_puts(buf, raw_new);
-
- git_signature__writebuf(buf, " ", committer);
-
- /* drop trailing LF */
- git_buf_rtrim(buf);
-
- if (msg) {
- git_buf_putc(buf, '\t');
- git_buf_puts(buf, msg);
- }
-
- git_buf_putc(buf, '\n');
-
- return git_buf_oom(buf);
-}
-
-static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char *refname)
-{
- git_repository *repo;
- git_buf log_path = GIT_BUF_INIT;
- int error;
-
- repo = backend->repo;
-
- if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
- giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname);
- return GIT_EINVALIDSPEC;
- }
-
- if (retrieve_reflog_path(&log_path, repo, refname) < 0)
- return -1;
-
- if (!git_path_isfile(git_buf_cstr(&log_path))) {
- giterr_set(GITERR_INVALID,
- "Log file for reference '%s' doesn't exist.", refname);
- error = -1;
- goto cleanup;
- }
-
- error = git_filebuf_open(file, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE);
-
-cleanup:
- git_buf_free(&log_path);
-
- return error;
-}
-
-static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
-{
- int error = -1;
- unsigned int i;
- git_reflog_entry *entry;
- refdb_fs_backend *backend;
- git_buf log = GIT_BUF_INIT;
- git_filebuf fbuf = GIT_FILEBUF_INIT;
-
- assert(_backend && reflog);
-
- backend = (refdb_fs_backend *) _backend;
-
- if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0)
- return -1;
-
- git_vector_foreach(&reflog->entries, i, entry) {
- if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
- goto cleanup;
-
- if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
- goto cleanup;
- }
-
- error = git_filebuf_commit(&fbuf);
- goto success;
-
-cleanup:
- git_filebuf_cleanup(&fbuf);
-
-success:
- git_buf_free(&log);
-
- return error;
-}
-
-/* Append to the reflog, must be called under reference lock */
-static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *who, const char *message)
-{
- int error, is_symbolic;
- git_oid old_id = {{0}}, new_id = {{0}};
- git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
- git_repository *repo = backend->repo;
-
- is_symbolic = ref->type == GIT_REF_SYMBOLIC;
-
- /* "normal" symbolic updates do not write */
- if (is_symbolic &&
- strcmp(ref->name, GIT_HEAD_FILE) &&
- !(old && new))
- return 0;
-
- /* From here on is_symoblic also means that it's HEAD */
-
- if (old) {
- git_oid_cpy(&old_id, old);
- } else {
- error = git_reference_name_to_id(&old_id, repo, ref->name);
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
- }
-
- if (new) {
- git_oid_cpy(&new_id, new);
- } else {
- if (!is_symbolic) {
- git_oid_cpy(&new_id, git_reference_target(ref));
- } else {
- error = git_reference_name_to_id(&new_id, repo, git_reference_symbolic_target(ref));
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
- /* detaching HEAD does not create an entry */
- if (error == GIT_ENOTFOUND)
- return 0;
-
- giterr_clear();
- }
- }
-
- if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0)
- goto cleanup;
-
- if ((error = retrieve_reflog_path(&path, repo, ref->name)) < 0)
- goto cleanup;
-
- if (((error = git_futils_mkpath2file(git_buf_cstr(&path), 0777)) < 0) &&
- (error != GIT_EEXISTS)) {
- goto cleanup;
- }
-
- /* If the new branch matches part of the namespace of a previously deleted branch,
- * there maybe an obsolete/unused directory (or directory hierarchy) in the way.
- */
- if (git_path_isdir(git_buf_cstr(&path))) {
- if ((error = git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) {
- if (error == GIT_ENOTFOUND)
- error = 0;
- } else if (git_path_isdir(git_buf_cstr(&path))) {
- giterr_set(GITERR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder",
- ref->name);
- error = GIT_EDIRECTORY;
- }
-
- if (error != 0)
- goto cleanup;
- }
-
- error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE);
-
-cleanup:
- git_buf_free(&buf);
- git_buf_free(&path);
-
- return error;
-}
-
-static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name)
-{
- int error = 0, fd;
- git_buf old_path = GIT_BUF_INIT;
- git_buf new_path = GIT_BUF_INIT;
- git_buf temp_path = GIT_BUF_INIT;
- git_buf normalized = GIT_BUF_INIT;
- git_repository *repo;
- refdb_fs_backend *backend;
-
- assert(_backend && old_name && new_name);
-
- backend = (refdb_fs_backend *) _backend;
- repo = backend->repo;
-
- if ((error = git_reference__normalize_name(
- &normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
- return error;
-
- if (git_buf_joinpath(&temp_path, repo->path_repository, GIT_REFLOG_DIR) < 0)
- return -1;
-
- if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), old_name) < 0)
- return -1;
-
- if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
- return -1;
-
- if (!git_path_exists(git_buf_cstr(&old_path))) {
- error = GIT_ENOTFOUND;
- goto cleanup;
- }
-
- /*
- * Move the reflog to a temporary place. This two-phase renaming is required
- * in order to cope with funny renaming use cases when one tries to move a reference
- * to a partially colliding namespace:
- * - a/b -> a/b/c
- * - a/b/c/d -> a/b/c
- */
- if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
- return -1;
-
- if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path), GIT_REFLOG_FILE_MODE)) < 0) {
- error = -1;
- goto cleanup;
- }
-
- p_close(fd);
-
- if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
- giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
- error = -1;
- goto cleanup;
- }
-
- if (git_path_isdir(git_buf_cstr(&new_path)) &&
- (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
- error = -1;
- goto cleanup;
- }
-
- if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
- error = -1;
- goto cleanup;
- }
-
- if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
- giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
- error = -1;
- }
-
-cleanup:
- git_buf_free(&temp_path);
- git_buf_free(&old_path);
- git_buf_free(&new_path);
- git_buf_free(&normalized);
-
- return error;
-}
-
-static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name)
-{
- int error;
- git_buf path = GIT_BUF_INIT;
-
- git_repository *repo;
- refdb_fs_backend *backend;
-
- assert(_backend && name);
-
- backend = (refdb_fs_backend *) _backend;
- repo = backend->repo;
-
- error = retrieve_reflog_path(&path, repo, name);
-
- if (!error && git_path_exists(path.ptr))
- error = p_unlink(path.ptr);
-
- git_buf_free(&path);
-
- return error;
-
-}
-
-int git_refdb_backend_fs(
- git_refdb_backend **backend_out,
- git_repository *repository)
-{
- int t = 0;
- git_buf path = GIT_BUF_INIT;
- refdb_fs_backend *backend;
-
- backend = git__calloc(1, sizeof(refdb_fs_backend));
- GITERR_CHECK_ALLOC(backend);
-
- backend->repo = repository;
-
- if (setup_namespace(&path, repository) < 0)
- goto fail;
-
- backend->path = git_buf_detach(&path);
-
- if (git_buf_joinpath(&path, backend->path, GIT_PACKEDREFS_FILE) < 0 ||
- git_sortedcache_new(
- &backend->refcache, offsetof(struct packref, name),
- NULL, NULL, packref_cmp, git_buf_cstr(&path)) < 0)
- goto fail;
-
- git_buf_free(&path);
-
- if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_IGNORECASE) && t) {
- backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
- backend->direach_flags |= GIT_PATH_DIR_IGNORE_CASE;
- }
- if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_PRECOMPOSE) && t) {
- backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
- backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
- }
-
- backend->parent.exists = &refdb_fs_backend__exists;
- backend->parent.lookup = &refdb_fs_backend__lookup;
- backend->parent.iterator = &refdb_fs_backend__iterator;
- backend->parent.write = &refdb_fs_backend__write;
- backend->parent.del = &refdb_fs_backend__delete;
- backend->parent.rename = &refdb_fs_backend__rename;
- backend->parent.compress = &refdb_fs_backend__compress;
- backend->parent.lock = &refdb_fs_backend__lock;
- backend->parent.unlock = &refdb_fs_backend__unlock;
- backend->parent.has_log = &refdb_reflog_fs__has_log;
- backend->parent.ensure_log = &refdb_reflog_fs__ensure_log;
- backend->parent.free = &refdb_fs_backend__free;
- backend->parent.reflog_read = &refdb_reflog_fs__read;
- backend->parent.reflog_write = &refdb_reflog_fs__write;
- backend->parent.reflog_rename = &refdb_reflog_fs__rename;
- backend->parent.reflog_delete = &refdb_reflog_fs__delete;
-
- *backend_out = (git_refdb_backend *)backend;
- return 0;
-
-fail:
- git_buf_free(&path);
- git__free(backend->path);
- git__free(backend);
- return -1;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_refdb_fs_h__
-#define INCLUDE_refdb_fs_h__
-
-typedef struct {
- git_strmap *packfile;
- time_t packfile_time;
-} git_refcache;
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "reflog.h"
-#include "repository.h"
-#include "filebuf.h"
-#include "signature.h"
-#include "refdb.h"
-
-#include <git2/sys/refdb_backend.h>
-
-git_reflog_entry *git_reflog_entry__alloc(void)
-{
- return git__calloc(1, sizeof(git_reflog_entry));
-}
-
-void git_reflog_entry__free(git_reflog_entry *entry)
-{
- git_signature_free(entry->committer);
-
- git__free(entry->msg);
- git__free(entry);
-}
-
-void git_reflog_free(git_reflog *reflog)
-{
- size_t i;
- git_reflog_entry *entry;
-
- if (reflog == NULL)
- return;
-
- if (reflog->db)
- GIT_REFCOUNT_DEC(reflog->db, git_refdb__free);
-
- for (i=0; i < reflog->entries.length; i++) {
- entry = git_vector_get(&reflog->entries, i);
-
- git_reflog_entry__free(entry);
- }
-
- git_vector_free(&reflog->entries);
- git__free(reflog->ref_name);
- git__free(reflog);
-}
-
-int git_reflog_read(git_reflog **reflog, git_repository *repo, const char *name)
-{
- git_refdb *refdb;
- int error;
-
- assert(reflog && repo && name);
-
- if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
- return error;
-
- return git_refdb_reflog_read(reflog, refdb, name);
-}
-
-int git_reflog_write(git_reflog *reflog)
-{
- git_refdb *db;
-
- assert(reflog && reflog->db);
-
- db = reflog->db;
- return db->backend->reflog_write(db->backend, reflog);
-}
-
-int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg)
-{
- git_reflog_entry *entry;
- const git_reflog_entry *previous;
- const char *newline;
-
- assert(reflog && new_oid && committer);
-
- entry = git__calloc(1, sizeof(git_reflog_entry));
- GITERR_CHECK_ALLOC(entry);
-
- if ((git_signature_dup(&entry->committer, committer)) < 0)
- goto cleanup;
-
- if (msg != NULL) {
- if ((entry->msg = git__strdup(msg)) == NULL)
- goto cleanup;
-
- newline = strchr(msg, '\n');
-
- if (newline) {
- if (newline[1] != '\0') {
- giterr_set(GITERR_INVALID, "Reflog message cannot contain newline");
- goto cleanup;
- }
-
- entry->msg[newline - msg] = '\0';
- }
- }
-
- previous = git_reflog_entry_byindex(reflog, 0);
-
- if (previous == NULL)
- git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO);
- else
- git_oid_cpy(&entry->oid_old, &previous->oid_cur);
-
- git_oid_cpy(&entry->oid_cur, new_oid);
-
- if (git_vector_insert(&reflog->entries, entry) < 0)
- goto cleanup;
-
- return 0;
-
-cleanup:
- git_reflog_entry__free(entry);
- return -1;
-}
-
-int git_reflog_rename(git_repository *repo, const char *old_name, const char *new_name)
-{
- git_refdb *refdb;
- int error;
-
- if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
- return -1;
-
- return refdb->backend->reflog_rename(refdb->backend, old_name, new_name);
-}
-
-int git_reflog_delete(git_repository *repo, const char *name)
-{
- git_refdb *refdb;
- int error;
-
- if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
- return -1;
-
- return refdb->backend->reflog_delete(refdb->backend, name);
-}
-
-size_t git_reflog_entrycount(git_reflog *reflog)
-{
- assert(reflog);
- return reflog->entries.length;
-}
-
-const git_reflog_entry * git_reflog_entry_byindex(const git_reflog *reflog, size_t idx)
-{
- assert(reflog);
-
- if (idx >= reflog->entries.length)
- return NULL;
-
- return git_vector_get(
- &reflog->entries, reflog_inverse_index(idx, reflog->entries.length));
-}
-
-const git_oid * git_reflog_entry_id_old(const git_reflog_entry *entry)
-{
- assert(entry);
- return &entry->oid_old;
-}
-
-const git_oid * git_reflog_entry_id_new(const git_reflog_entry *entry)
-{
- assert(entry);
- return &entry->oid_cur;
-}
-
-const git_signature * git_reflog_entry_committer(const git_reflog_entry *entry)
-{
- assert(entry);
- return entry->committer;
-}
-
-const char * git_reflog_entry_message(const git_reflog_entry *entry)
-{
- assert(entry);
- return entry->msg;
-}
-
-int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
-{
- size_t entrycount;
- git_reflog_entry *entry, *previous;
-
- entrycount = git_reflog_entrycount(reflog);
-
- entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
-
- if (entry == NULL) {
- giterr_set(GITERR_REFERENCE, "No reflog entry at index %"PRIuZ, idx);
- return GIT_ENOTFOUND;
- }
-
- git_reflog_entry__free(entry);
-
- if (git_vector_remove(
- &reflog->entries, reflog_inverse_index(idx, entrycount)) < 0)
- return -1;
-
- if (!rewrite_previous_entry)
- return 0;
-
- /* No need to rewrite anything when removing the most recent entry */
- if (idx == 0)
- return 0;
-
- /* Have the latest entry just been dropped? */
- if (entrycount == 1)
- return 0;
-
- entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx - 1);
-
- /* If the oldest entry has just been removed... */
- if (idx == entrycount - 1) {
- /* ...clear the oid_old member of the "new" oldest entry */
- if (git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO) < 0)
- return -1;
-
- return 0;
- }
-
- previous = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
- git_oid_cpy(&entry->oid_old, &previous->oid_cur);
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_reflog_h__
-#define INCLUDE_reflog_h__
-
-#include "common.h"
-#include "git2/reflog.h"
-#include "vector.h"
-
-#define GIT_REFLOG_DIR "logs/"
-#define GIT_REFLOG_DIR_MODE 0777
-#define GIT_REFLOG_FILE_MODE 0666
-
-#define GIT_REFLOG_SIZE_MIN (2*GIT_OID_HEXSZ+2+17)
-
-struct git_reflog_entry {
- git_oid oid_old;
- git_oid oid_cur;
-
- git_signature *committer;
-
- char *msg;
-};
-
-struct git_reflog {
- git_refdb *db;
- char *ref_name;
- git_vector entries;
-};
-
-GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
-{
- return (total - 1) - idx;
-}
-
-#endif /* INCLUDE_reflog_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "refs.h"
-#include "hash.h"
-#include "repository.h"
-#include "fileops.h"
-#include "filebuf.h"
-#include "pack.h"
-#include "reflog.h"
-#include "refdb.h"
-
-#include <git2/tag.h>
-#include <git2/object.h>
-#include <git2/oid.h>
-#include <git2/branch.h>
-#include <git2/refs.h>
-#include <git2/refdb.h>
-#include <git2/sys/refs.h>
-#include <git2/signature.h>
-#include <git2/commit.h>
-
-GIT__USE_STRMAP
-
-#define DEFAULT_NESTING_LEVEL 5
-#define MAX_NESTING_LEVEL 10
-
-enum {
- GIT_PACKREF_HAS_PEEL = 1,
- GIT_PACKREF_WAS_LOOSE = 2
-};
-
-static git_reference *alloc_ref(const char *name)
-{
- git_reference *ref = NULL;
- size_t namelen = strlen(name), reflen;
-
- if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) &&
- !GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) &&
- (ref = git__calloc(1, reflen)) != NULL)
- memcpy(ref->name, name, namelen + 1);
-
- return ref;
-}
-
-git_reference *git_reference__alloc_symbolic(
- const char *name, const char *target)
-{
- git_reference *ref;
-
- assert(name && target);
-
- ref = alloc_ref(name);
- if (!ref)
- return NULL;
-
- ref->type = GIT_REF_SYMBOLIC;
-
- if ((ref->target.symbolic = git__strdup(target)) == NULL) {
- git__free(ref);
- return NULL;
- }
-
- return ref;
-}
-
-git_reference *git_reference__alloc(
- const char *name,
- const git_oid *oid,
- const git_oid *peel)
-{
- git_reference *ref;
-
- assert(name && oid);
-
- ref = alloc_ref(name);
- if (!ref)
- return NULL;
-
- ref->type = GIT_REF_OID;
- git_oid_cpy(&ref->target.oid, oid);
-
- if (peel != NULL)
- git_oid_cpy(&ref->peel, peel);
-
- return ref;
-}
-
-git_reference *git_reference__set_name(
- git_reference *ref, const char *name)
-{
- size_t namelen = strlen(name);
- size_t reflen;
- git_reference *rewrite = NULL;
-
- if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) &&
- !GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) &&
- (rewrite = git__realloc(ref, reflen)) != NULL)
- memcpy(rewrite->name, name, namelen + 1);
-
- return rewrite;
-}
-
-int git_reference_dup(git_reference **dest, git_reference *source)
-{
- if (source->type == GIT_REF_SYMBOLIC)
- *dest = git_reference__alloc_symbolic(source->name, source->target.symbolic);
- else
- *dest = git_reference__alloc(source->name, &source->target.oid, &source->peel);
-
- GITERR_CHECK_ALLOC(*dest);
-
- return 0;
-}
-
-void git_reference_free(git_reference *reference)
-{
- if (reference == NULL)
- return;
-
- if (reference->type == GIT_REF_SYMBOLIC)
- git__free(reference->target.symbolic);
-
- if (reference->db)
- GIT_REFCOUNT_DEC(reference->db, git_refdb__free);
-
- git__free(reference);
-}
-
-int git_reference_delete(git_reference *ref)
-{
- const git_oid *old_id = NULL;
- const char *old_target = NULL;
-
- if (ref->type == GIT_REF_OID)
- old_id = &ref->target.oid;
- else
- old_target = ref->target.symbolic;
-
- return git_refdb_delete(ref->db, ref->name, old_id, old_target);
-}
-
-int git_reference_remove(git_repository *repo, const char *name)
-{
- git_refdb *db;
- int error;
-
- if ((error = git_repository_refdb__weakptr(&db, repo)) < 0)
- return error;
-
- return git_refdb_delete(db, name, NULL, NULL);
-}
-
-int git_reference_lookup(git_reference **ref_out,
- git_repository *repo, const char *name)
-{
- return git_reference_lookup_resolved(ref_out, repo, name, 0);
-}
-
-int git_reference_name_to_id(
- git_oid *out, git_repository *repo, const char *name)
-{
- int error;
- git_reference *ref;
-
- if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
- return error;
-
- git_oid_cpy(out, git_reference_target(ref));
- git_reference_free(ref);
- return 0;
-}
-
-static int reference_normalize_for_repo(
- git_refname_t out,
- git_repository *repo,
- const char *name)
-{
- int precompose;
- unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;
-
- if (!git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) &&
- precompose)
- flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
-
- return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
-}
-
-int git_reference_lookup_resolved(
- git_reference **ref_out,
- git_repository *repo,
- const char *name,
- int max_nesting)
-{
- git_refname_t scan_name;
- git_ref_t scan_type;
- int error = 0, nesting;
- git_reference *ref = NULL;
- git_refdb *refdb;
-
- assert(ref_out && repo && name);
-
- *ref_out = NULL;
-
- if (max_nesting > MAX_NESTING_LEVEL)
- max_nesting = MAX_NESTING_LEVEL;
- else if (max_nesting < 0)
- max_nesting = DEFAULT_NESTING_LEVEL;
-
- scan_type = GIT_REF_SYMBOLIC;
-
- if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0)
- return error;
-
- if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
- return error;
-
- for (nesting = max_nesting;
- nesting >= 0 && scan_type == GIT_REF_SYMBOLIC;
- nesting--)
- {
- if (nesting != max_nesting) {
- strncpy(scan_name, ref->target.symbolic, sizeof(scan_name));
- git_reference_free(ref);
- }
-
- if ((error = git_refdb_lookup(&ref, refdb, scan_name)) < 0)
- return error;
-
- scan_type = ref->type;
- }
-
- if (scan_type != GIT_REF_OID && max_nesting != 0) {
- giterr_set(GITERR_REFERENCE,
- "Cannot resolve reference (>%u levels deep)", max_nesting);
- git_reference_free(ref);
- return -1;
- }
-
- *ref_out = ref;
- return 0;
-}
-
-int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname)
-{
- int error = 0, i;
- bool fallbackmode = true, foundvalid = false;
- git_reference *ref;
- git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT;
-
- static const char* formatters[] = {
- "%s",
- GIT_REFS_DIR "%s",
- GIT_REFS_TAGS_DIR "%s",
- GIT_REFS_HEADS_DIR "%s",
- GIT_REFS_REMOTES_DIR "%s",
- GIT_REFS_REMOTES_DIR "%s/" GIT_HEAD_FILE,
- NULL
- };
-
- if (*refname)
- git_buf_puts(&name, refname);
- else {
- git_buf_puts(&name, GIT_HEAD_FILE);
- fallbackmode = false;
- }
-
- for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) {
-
- git_buf_clear(&refnamebuf);
-
- if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0)
- goto cleanup;
-
- if (!git_reference_is_valid_name(git_buf_cstr(&refnamebuf))) {
- error = GIT_EINVALIDSPEC;
- continue;
- }
- foundvalid = true;
-
- error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1);
-
- if (!error) {
- *out = ref;
- error = 0;
- goto cleanup;
- }
-
- if (error != GIT_ENOTFOUND)
- goto cleanup;
- }
-
-cleanup:
- if (error && !foundvalid) {
- /* never found a valid reference name */
- giterr_set(GITERR_REFERENCE,
- "Could not use '%s' as valid reference name", git_buf_cstr(&name));
- }
-
- if (error == GIT_ENOTFOUND)
- giterr_set(GITERR_REFERENCE, "no reference found for shorthand '%s'", refname);
-
- git_buf_free(&name);
- git_buf_free(&refnamebuf);
- return error;
-}
-
-/**
- * Getters
- */
-git_ref_t git_reference_type(const git_reference *ref)
-{
- assert(ref);
- return ref->type;
-}
-
-const char *git_reference_name(const git_reference *ref)
-{
- assert(ref);
- return ref->name;
-}
-
-git_repository *git_reference_owner(const git_reference *ref)
-{
- assert(ref);
- return ref->db->repo;
-}
-
-const git_oid *git_reference_target(const git_reference *ref)
-{
- assert(ref);
-
- if (ref->type != GIT_REF_OID)
- return NULL;
-
- return &ref->target.oid;
-}
-
-const git_oid *git_reference_target_peel(const git_reference *ref)
-{
- assert(ref);
-
- if (ref->type != GIT_REF_OID || git_oid_iszero(&ref->peel))
- return NULL;
-
- return &ref->peel;
-}
-
-const char *git_reference_symbolic_target(const git_reference *ref)
-{
- assert(ref);
-
- if (ref->type != GIT_REF_SYMBOLIC)
- return NULL;
-
- return ref->target.symbolic;
-}
-
-static int reference__create(
- git_reference **ref_out,
- git_repository *repo,
- const char *name,
- const git_oid *oid,
- const char *symbolic,
- int force,
- const git_signature *signature,
- const char *log_message,
- const git_oid *old_id,
- const char *old_target)
-{
- git_refname_t normalized;
- git_refdb *refdb;
- git_reference *ref = NULL;
- int error = 0;
-
- assert(repo && name);
- assert(symbolic || signature);
-
- if (ref_out)
- *ref_out = NULL;
-
- error = reference_normalize_for_repo(normalized, repo, name);
- if (error < 0)
- return error;
-
- error = git_repository_refdb__weakptr(&refdb, repo);
- if (error < 0)
- return error;
-
- if (oid != NULL) {
- assert(symbolic == NULL);
-
- if (!git_object__is_valid(repo, oid, GIT_OBJ_ANY)) {
- giterr_set(GITERR_REFERENCE,
- "Target OID for the reference doesn't exist on the repository");
- return -1;
- }
-
- ref = git_reference__alloc(normalized, oid, NULL);
- } else {
- git_refname_t normalized_target;
-
- if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0)
- return error;
-
- ref = git_reference__alloc_symbolic(normalized, normalized_target);
- }
-
- GITERR_CHECK_ALLOC(ref);
-
- if ((error = git_refdb_write(refdb, ref, force, signature, log_message, old_id, old_target)) < 0) {
- git_reference_free(ref);
- return error;
- }
-
- if (ref_out == NULL)
- git_reference_free(ref);
- else
- *ref_out = ref;
-
- return 0;
-}
-
-int configured_ident(git_signature **out, const git_repository *repo)
-{
- if (repo->ident_name && repo->ident_email)
- return git_signature_now(out, repo->ident_name, repo->ident_email);
-
- /* if not configured let us fall-through to the next method */
- return -1;
-}
-
-int git_reference__log_signature(git_signature **out, git_repository *repo)
-{
- int error;
- git_signature *who;
-
- if(((error = configured_ident(&who, repo)) < 0) &&
- ((error = git_signature_default(&who, repo)) < 0) &&
- ((error = git_signature_now(&who, "unknown", "unknown")) < 0))
- return error;
-
- *out = who;
- return 0;
-}
-
-int git_reference_create_matching(
- git_reference **ref_out,
- git_repository *repo,
- const char *name,
- const git_oid *id,
- int force,
- const git_oid *old_id,
- const char *log_message)
-
-{
- int error;
- git_signature *who = NULL;
-
- assert(id);
-
- if ((error = git_reference__log_signature(&who, repo)) < 0)
- return error;
-
- error = reference__create(
- ref_out, repo, name, id, NULL, force, who, log_message, old_id, NULL);
-
- git_signature_free(who);
- return error;
-}
-
-int git_reference_create(
- git_reference **ref_out,
- git_repository *repo,
- const char *name,
- const git_oid *id,
- int force,
- const char *log_message)
-{
- return git_reference_create_matching(ref_out, repo, name, id, force, NULL, log_message);
-}
-
-int git_reference_symbolic_create_matching(
- git_reference **ref_out,
- git_repository *repo,
- const char *name,
- const char *target,
- int force,
- const char *old_target,
- const char *log_message)
-{
- int error;
- git_signature *who = NULL;
-
- assert(target);
-
- if ((error = git_reference__log_signature(&who, repo)) < 0)
- return error;
-
- error = reference__create(
- ref_out, repo, name, NULL, target, force, who, log_message, NULL, old_target);
-
- git_signature_free(who);
- return error;
-}
-
-int git_reference_symbolic_create(
- git_reference **ref_out,
- git_repository *repo,
- const char *name,
- const char *target,
- int force,
- const char *log_message)
-{
- return git_reference_symbolic_create_matching(ref_out, repo, name, target, force, NULL, log_message);
-}
-
-static int ensure_is_an_updatable_direct_reference(git_reference *ref)
-{
- if (ref->type == GIT_REF_OID)
- return 0;
-
- giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
- return -1;
-}
-
-int git_reference_set_target(
- git_reference **out,
- git_reference *ref,
- const git_oid *id,
- const char *log_message)
-{
- int error;
- git_repository *repo;
-
- assert(out && ref && id);
-
- repo = ref->db->repo;
-
- if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
- return error;
-
- return git_reference_create_matching(out, repo, ref->name, id, 1, &ref->target.oid, log_message);
-}
-
-static int ensure_is_an_updatable_symbolic_reference(git_reference *ref)
-{
- if (ref->type == GIT_REF_SYMBOLIC)
- return 0;
-
- giterr_set(GITERR_REFERENCE, "Cannot set symbolic target on a direct reference");
- return -1;
-}
-
-int git_reference_symbolic_set_target(
- git_reference **out,
- git_reference *ref,
- const char *target,
- const char *log_message)
-{
- int error;
-
- assert(out && ref && target);
-
- if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0)
- return error;
-
- return git_reference_symbolic_create_matching(
- out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message);
-}
-
-static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
- const git_signature *signature, const char *message)
-{
- git_refname_t normalized;
- bool should_head_be_updated = false;
- int error = 0;
-
- assert(ref && new_name && signature);
-
- if ((error = reference_normalize_for_repo(
- normalized, git_reference_owner(ref), new_name)) < 0)
- return error;
-
-
- /* Check if we have to update HEAD. */
- if ((error = git_branch_is_head(ref)) < 0)
- return error;
-
- should_head_be_updated = (error > 0);
-
- if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
- return error;
-
- /* Update HEAD it was pointing to the reference being renamed */
- if (should_head_be_updated &&
- (error = git_repository_set_head(ref->db->repo, normalized)) < 0) {
- giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
- return error;
- }
-
- return 0;
-}
-
-
-int git_reference_rename(
- git_reference **out,
- git_reference *ref,
- const char *new_name,
- int force,
- const char *log_message)
-{
- git_signature *who;
- int error;
-
- if ((error = git_reference__log_signature(&who, ref->db->repo)) < 0)
- return error;
-
- error = reference__rename(out, ref, new_name, force, who, log_message);
- git_signature_free(who);
-
- return error;
-}
-
-int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
-{
- switch (git_reference_type(ref)) {
- case GIT_REF_OID:
- return git_reference_lookup(ref_out, ref->db->repo, ref->name);
-
- case GIT_REF_SYMBOLIC:
- return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1);
-
- default:
- giterr_set(GITERR_REFERENCE, "Invalid reference");
- return -1;
- }
-}
-
-int git_reference_foreach(
- git_repository *repo,
- git_reference_foreach_cb callback,
- void *payload)
-{
- git_reference_iterator *iter;
- git_reference *ref;
- int error;
-
- if ((error = git_reference_iterator_new(&iter, repo)) < 0)
- return error;
-
- while (!(error = git_reference_next(&ref, iter))) {
- if ((error = callback(ref, payload)) != 0) {
- giterr_set_after_callback(error);
- break;
- }
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- git_reference_iterator_free(iter);
- return error;
-}
-
-int git_reference_foreach_name(
- git_repository *repo,
- git_reference_foreach_name_cb callback,
- void *payload)
-{
- git_reference_iterator *iter;
- const char *refname;
- int error;
-
- if ((error = git_reference_iterator_new(&iter, repo)) < 0)
- return error;
-
- while (!(error = git_reference_next_name(&refname, iter))) {
- if ((error = callback(refname, payload)) != 0) {
- giterr_set_after_callback(error);
- break;
- }
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- git_reference_iterator_free(iter);
- return error;
-}
-
-int git_reference_foreach_glob(
- git_repository *repo,
- const char *glob,
- git_reference_foreach_name_cb callback,
- void *payload)
-{
- git_reference_iterator *iter;
- const char *refname;
- int error;
-
- if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0)
- return error;
-
- while (!(error = git_reference_next_name(&refname, iter))) {
- if ((error = callback(refname, payload)) != 0) {
- giterr_set_after_callback(error);
- break;
- }
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- git_reference_iterator_free(iter);
- return error;
-}
-
-int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo)
-{
- git_refdb *refdb;
-
- if (git_repository_refdb__weakptr(&refdb, repo) < 0)
- return -1;
-
- return git_refdb_iterator(out, refdb, NULL);
-}
-
-int git_reference_iterator_glob_new(
- git_reference_iterator **out, git_repository *repo, const char *glob)
-{
- git_refdb *refdb;
-
- if (git_repository_refdb__weakptr(&refdb, repo) < 0)
- return -1;
-
- return git_refdb_iterator(out, refdb, glob);
-}
-
-int git_reference_next(git_reference **out, git_reference_iterator *iter)
-{
- return git_refdb_iterator_next(out, iter);
-}
-
-int git_reference_next_name(const char **out, git_reference_iterator *iter)
-{
- return git_refdb_iterator_next_name(out, iter);
-}
-
-void git_reference_iterator_free(git_reference_iterator *iter)
-{
- if (iter == NULL)
- return;
-
- git_refdb_iterator_free(iter);
-}
-
-static int cb__reflist_add(const char *ref, void *data)
-{
- char *name = git__strdup(ref);
- GITERR_CHECK_ALLOC(name);
- return git_vector_insert((git_vector *)data, name);
-}
-
-int git_reference_list(
- git_strarray *array,
- git_repository *repo)
-{
- git_vector ref_list;
-
- assert(array && repo);
-
- array->strings = NULL;
- array->count = 0;
-
- if (git_vector_init(&ref_list, 8, NULL) < 0)
- return -1;
-
- if (git_reference_foreach_name(
- repo, &cb__reflist_add, (void *)&ref_list) < 0) {
- git_vector_free(&ref_list);
- return -1;
- }
-
- array->strings = (char **)git_vector_detach(&array->count, NULL, &ref_list);
-
- return 0;
-}
-
-static int is_valid_ref_char(char ch)
-{
- if ((unsigned) ch <= ' ')
- return 0;
-
- switch (ch) {
- case '~':
- case '^':
- case ':':
- case '\\':
- case '?':
- case '[':
- case '*':
- return 0;
- default:
- return 1;
- }
-}
-
-static int ensure_segment_validity(const char *name)
-{
- const char *current = name;
- char prev = '\0';
- const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION);
- int segment_len;
-
- if (*current == '.')
- return -1; /* Refname starts with "." */
-
- for (current = name; ; current++) {
- if (*current == '\0' || *current == '/')
- break;
-
- if (!is_valid_ref_char(*current))
- return -1; /* Illegal character in refname */
-
- if (prev == '.' && *current == '.')
- return -1; /* Refname contains ".." */
-
- if (prev == '@' && *current == '{')
- return -1; /* Refname contains "@{" */
-
- prev = *current;
- }
-
- segment_len = (int)(current - name);
-
- /* A refname component can not end with ".lock" */
- if (segment_len >= lock_len &&
- !memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len))
- return -1;
-
- return segment_len;
-}
-
-static bool is_all_caps_and_underscore(const char *name, size_t len)
-{
- size_t i;
- char c;
-
- assert(name && len > 0);
-
- for (i = 0; i < len; i++)
- {
- c = name[i];
- if ((c < 'A' || c > 'Z') && c != '_')
- return false;
- }
-
- if (*name == '_' || name[len - 1] == '_')
- return false;
-
- return true;
-}
-
-/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
-int git_reference__normalize_name(
- git_buf *buf,
- const char *name,
- unsigned int flags)
-{
- const char *current;
- int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
- unsigned int process_flags;
- bool normalize = (buf != NULL);
-
-#ifdef GIT_USE_ICONV
- git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
-#endif
-
- assert(name);
-
- process_flags = flags;
- current = (char *)name;
-
- if (*current == '/')
- goto cleanup;
-
- if (normalize)
- git_buf_clear(buf);
-
-#ifdef GIT_USE_ICONV
- if ((flags & GIT_REF_FORMAT__PRECOMPOSE_UNICODE) != 0) {
- size_t namelen = strlen(current);
- if ((error = git_path_iconv_init_precompose(&ic)) < 0 ||
- (error = git_path_iconv(&ic, ¤t, &namelen)) < 0)
- goto cleanup;
- error = GIT_EINVALIDSPEC;
- }
-#endif
-
- while (true) {
- segment_len = ensure_segment_validity(current);
- if (segment_len < 0) {
- if ((process_flags & GIT_REF_FORMAT_REFSPEC_PATTERN) &&
- current[0] == '*' &&
- (current[1] == '\0' || current[1] == '/')) {
- /* Accept one wildcard as a full refname component. */
- process_flags &= ~GIT_REF_FORMAT_REFSPEC_PATTERN;
- segment_len = 1;
- } else
- goto cleanup;
- }
-
- if (segment_len > 0) {
- if (normalize) {
- size_t cur_len = git_buf_len(buf);
-
- git_buf_joinpath(buf, git_buf_cstr(buf), current);
- git_buf_truncate(buf,
- cur_len + segment_len + (segments_count ? 1 : 0));
-
- if (git_buf_oom(buf)) {
- error = -1;
- goto cleanup;
- }
- }
-
- segments_count++;
- }
-
- /* No empty segment is allowed when not normalizing */
- if (segment_len == 0 && !normalize)
- goto cleanup;
-
- if (current[segment_len] == '\0')
- break;
-
- current += segment_len + 1;
- }
-
- /* A refname can not be empty */
- if (segment_len == 0 && segments_count == 0)
- goto cleanup;
-
- /* A refname can not end with "." */
- if (current[segment_len - 1] == '.')
- goto cleanup;
-
- /* A refname can not end with "/" */
- if (current[segment_len - 1] == '/')
- goto cleanup;
-
- if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL))
- goto cleanup;
-
- if ((segments_count == 1 ) &&
- !(flags & GIT_REF_FORMAT_REFSPEC_SHORTHAND) &&
- !(is_all_caps_and_underscore(name, (size_t)segment_len) ||
- ((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name))))
- goto cleanup;
-
- if ((segments_count > 1)
- && (is_all_caps_and_underscore(name, strchr(name, '/') - name)))
- goto cleanup;
-
- error = 0;
-
-cleanup:
- if (error == GIT_EINVALIDSPEC)
- giterr_set(
- GITERR_REFERENCE,
- "The given reference name '%s' is not valid", name);
-
- if (error && normalize)
- git_buf_free(buf);
-
-#ifdef GIT_USE_ICONV
- git_path_iconv_clear(&ic);
-#endif
-
- return error;
-}
-
-int git_reference_normalize_name(
- char *buffer_out,
- size_t buffer_size,
- const char *name,
- unsigned int flags)
-{
- git_buf buf = GIT_BUF_INIT;
- int error;
-
- if ((error = git_reference__normalize_name(&buf, name, flags)) < 0)
- goto cleanup;
-
- if (git_buf_len(&buf) > buffer_size - 1) {
- giterr_set(
- GITERR_REFERENCE,
- "The provided buffer is too short to hold the normalization of '%s'", name);
- error = GIT_EBUFS;
- goto cleanup;
- }
-
- git_buf_copy_cstr(buffer_out, buffer_size, &buf);
-
- error = 0;
-
-cleanup:
- git_buf_free(&buf);
- return error;
-}
-
-#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
-
-int git_reference_cmp(
- const git_reference *ref1,
- const git_reference *ref2)
-{
- git_ref_t type1, type2;
- assert(ref1 && ref2);
-
- type1 = git_reference_type(ref1);
- type2 = git_reference_type(ref2);
-
- /* let's put symbolic refs before OIDs */
- if (type1 != type2)
- return (type1 == GIT_REF_SYMBOLIC) ? -1 : 1;
-
- if (type1 == GIT_REF_SYMBOLIC)
- return strcmp(ref1->target.symbolic, ref2->target.symbolic);
-
- return git_oid__cmp(&ref1->target.oid, &ref2->target.oid);
-}
-
-/**
- * Get the end of a chain of references. If the final one is not
- * found, we return the reference just before that.
- */
-static int get_terminal(git_reference **out, git_repository *repo, const char *ref_name, int nesting)
-{
- git_reference *ref;
- int error = 0;
-
- if (nesting > MAX_NESTING_LEVEL) {
- giterr_set(GITERR_REFERENCE, "Reference chain too deep (%d)", nesting);
- return GIT_ENOTFOUND;
- }
-
- /* set to NULL to let the caller know that they're at the end of the chain */
- if ((error = git_reference_lookup(&ref, repo, ref_name)) < 0) {
- *out = NULL;
- return error;
- }
-
- if (git_reference_type(ref) == GIT_REF_OID) {
- *out = ref;
- error = 0;
- } else {
- error = get_terminal(out, repo, git_reference_symbolic_target(ref), nesting + 1);
- if (error == GIT_ENOTFOUND && !*out)
- *out = ref;
- else
- git_reference_free(ref);
- }
-
- return error;
-}
-
-/*
- * Starting with the reference given by `ref_name`, follows symbolic
- * references until a direct reference is found and updated the OID
- * on that direct reference to `oid`.
- */
-int git_reference__update_terminal(
- git_repository *repo,
- const char *ref_name,
- const git_oid *oid,
- const git_signature *sig,
- const char *log_message)
-{
- git_reference *ref = NULL, *ref2 = NULL;
- git_signature *who = NULL;
- const git_signature *to_use;
- int error = 0;
-
- if (!sig && (error = git_reference__log_signature(&who, repo)) < 0)
- return error;
-
- to_use = sig ? sig : who;
- error = get_terminal(&ref, repo, ref_name, 0);
-
- /* found a dangling symref */
- if (error == GIT_ENOTFOUND && ref) {
- assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
- giterr_clear();
- error = reference__create(&ref2, repo, ref->target.symbolic, oid, NULL, 0, to_use,
- log_message, NULL, NULL);
- } else if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = reference__create(&ref2, repo, ref_name, oid, NULL, 0, to_use,
- log_message, NULL, NULL);
- } else if (error == 0) {
- assert(git_reference_type(ref) == GIT_REF_OID);
- error = reference__create(&ref2, repo, ref->name, oid, NULL, 1, to_use,
- log_message, &ref->target.oid, NULL);
- }
-
- git_reference_free(ref2);
- git_reference_free(ref);
- git_signature_free(who);
- return error;
-}
-
-int git_reference__update_for_commit(
- git_repository *repo,
- git_reference *ref,
- const char *ref_name,
- const git_oid *id,
- const char *operation)
-{
- git_reference *ref_new = NULL;
- git_commit *commit = NULL;
- git_buf reflog_msg = GIT_BUF_INIT;
- const git_signature *who;
- int error;
-
- if ((error = git_commit_lookup(&commit, repo, id)) < 0 ||
- (error = git_buf_printf(&reflog_msg, "%s%s: %s",
- operation ? operation : "commit",
- git_commit_parentcount(commit) == 0 ? " (initial)" : "",
- git_commit_summary(commit))) < 0)
- goto done;
-
- who = git_commit_committer(commit);
-
- if (ref) {
- if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
- return error;
-
- error = reference__create(&ref_new, repo, ref->name, id, NULL, 1, who,
- git_buf_cstr(&reflog_msg), &ref->target.oid, NULL);
- }
- else
- error = git_reference__update_terminal(
- repo, ref_name, id, who, git_buf_cstr(&reflog_msg));
-
-done:
- git_reference_free(ref_new);
- git_buf_free(&reflog_msg);
- git_commit_free(commit);
- return error;
-}
-
-int git_reference_has_log(git_repository *repo, const char *refname)
-{
- int error;
- git_refdb *refdb;
-
- assert(repo && refname);
-
- if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
- return error;
-
- return git_refdb_has_log(refdb, refname);
-}
-
-int git_reference_ensure_log(git_repository *repo, const char *refname)
-{
- int error;
- git_refdb *refdb;
-
- assert(repo && refname);
-
- if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
- return error;
-
- return git_refdb_ensure_log(refdb, refname);
-}
-
-int git_reference__is_branch(const char *ref_name)
-{
- return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0;
-}
-
-int git_reference_is_branch(const git_reference *ref)
-{
- assert(ref);
- return git_reference__is_branch(ref->name);
-}
-
-int git_reference__is_remote(const char *ref_name)
-{
- return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0;
-}
-
-int git_reference_is_remote(const git_reference *ref)
-{
- assert(ref);
- return git_reference__is_remote(ref->name);
-}
-
-int git_reference__is_tag(const char *ref_name)
-{
- return git__prefixcmp(ref_name, GIT_REFS_TAGS_DIR) == 0;
-}
-
-int git_reference_is_tag(const git_reference *ref)
-{
- assert(ref);
- return git_reference__is_tag(ref->name);
-}
-
-int git_reference__is_note(const char *ref_name)
-{
- return git__prefixcmp(ref_name, GIT_REFS_NOTES_DIR) == 0;
-}
-
-int git_reference_is_note(const git_reference *ref)
-{
- assert(ref);
- return git_reference__is_note(ref->name);
-}
-
-static int peel_error(int error, git_reference *ref, const char* msg)
-{
- giterr_set(
- GITERR_INVALID,
- "The reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
- return error;
-}
-
-int git_reference_peel(
- git_object **peeled,
- git_reference *ref,
- git_otype target_type)
-{
- git_reference *resolved = NULL;
- git_object *target = NULL;
- int error;
-
- assert(ref);
-
- if (ref->type == GIT_REF_OID) {
- resolved = ref;
- } else {
- if ((error = git_reference_resolve(&resolved, ref)) < 0)
- return peel_error(error, ref, "Cannot resolve reference");
- }
-
- if (!git_oid_iszero(&resolved->peel)) {
- error = git_object_lookup(&target,
- git_reference_owner(ref), &resolved->peel, GIT_OBJ_ANY);
- } else {
- error = git_object_lookup(&target,
- git_reference_owner(ref), &resolved->target.oid, GIT_OBJ_ANY);
- }
-
- if (error < 0) {
- peel_error(error, ref, "Cannot retrieve reference target");
- goto cleanup;
- }
-
- if (target_type == GIT_OBJ_ANY && git_object_type(target) != GIT_OBJ_TAG)
- error = git_object_dup(peeled, target);
- else
- error = git_object_peel(peeled, target, target_type);
-
-cleanup:
- git_object_free(target);
-
- if (resolved != ref)
- git_reference_free(resolved);
-
- return error;
-}
-
-int git_reference__is_valid_name(const char *refname, unsigned int flags)
-{
- if (git_reference__normalize_name(NULL, refname, flags) < 0) {
- giterr_clear();
- return false;
- }
-
- return true;
-}
-
-int git_reference_is_valid_name(const char *refname)
-{
- return git_reference__is_valid_name(refname, GIT_REF_FORMAT_ALLOW_ONELEVEL);
-}
-
-const char *git_reference__shorthand(const char *name)
-{
- if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
- return name + strlen(GIT_REFS_HEADS_DIR);
- else if (!git__prefixcmp(name, GIT_REFS_TAGS_DIR))
- return name + strlen(GIT_REFS_TAGS_DIR);
- else if (!git__prefixcmp(name, GIT_REFS_REMOTES_DIR))
- return name + strlen(GIT_REFS_REMOTES_DIR);
- else if (!git__prefixcmp(name, GIT_REFS_DIR))
- return name + strlen(GIT_REFS_DIR);
-
- /* No shorthands are avaiable, so just return the name */
- return name;
-}
-
-const char *git_reference_shorthand(const git_reference *ref)
-{
- return git_reference__shorthand(ref->name);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_refs_h__
-#define INCLUDE_refs_h__
-
-#include "common.h"
-#include "git2/oid.h"
-#include "git2/refs.h"
-#include "git2/refdb.h"
-#include "strmap.h"
-#include "buffer.h"
-#include "oid.h"
-
-#define GIT_REFS_DIR "refs/"
-#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
-#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
-#define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/"
-#define GIT_REFS_NOTES_DIR GIT_REFS_DIR "notes/"
-#define GIT_REFS_DIR_MODE 0777
-#define GIT_REFS_FILE_MODE 0666
-
-#define GIT_RENAMED_REF_FILE GIT_REFS_DIR "RENAMED-REF"
-
-#define GIT_SYMREF "ref: "
-#define GIT_PACKEDREFS_FILE "packed-refs"
-#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled "
-#define GIT_PACKEDREFS_FILE_MODE 0666
-
-#define GIT_HEAD_FILE "HEAD"
-#define GIT_ORIG_HEAD_FILE "ORIG_HEAD"
-#define GIT_FETCH_HEAD_FILE "FETCH_HEAD"
-#define GIT_MERGE_HEAD_FILE "MERGE_HEAD"
-#define GIT_REVERT_HEAD_FILE "REVERT_HEAD"
-#define GIT_CHERRYPICK_HEAD_FILE "CHERRY_PICK_HEAD"
-#define GIT_BISECT_LOG_FILE "BISECT_LOG"
-#define GIT_REBASE_MERGE_DIR "rebase-merge/"
-#define GIT_REBASE_MERGE_INTERACTIVE_FILE GIT_REBASE_MERGE_DIR "interactive"
-#define GIT_REBASE_APPLY_DIR "rebase-apply/"
-#define GIT_REBASE_APPLY_REBASING_FILE GIT_REBASE_APPLY_DIR "rebasing"
-#define GIT_REBASE_APPLY_APPLYING_FILE GIT_REBASE_APPLY_DIR "applying"
-#define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master"
-
-#define GIT_SEQUENCER_DIR "sequencer/"
-#define GIT_SEQUENCER_HEAD_FILE GIT_SEQUENCER_DIR "head"
-#define GIT_SEQUENCER_OPTIONS_FILE GIT_SEQUENCER_DIR "options"
-#define GIT_SEQUENCER_TODO_FILE GIT_SEQUENCER_DIR "todo"
-
-#define GIT_STASH_FILE "stash"
-#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
-
-#define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16)
-
-#define GIT_REFNAME_MAX 1024
-
-typedef char git_refname_t[GIT_REFNAME_MAX];
-
-struct git_reference {
- git_refdb *db;
- git_ref_t type;
-
- union {
- git_oid oid;
- char *symbolic;
- } target;
-
- git_oid peel;
- char name[GIT_FLEX_ARRAY];
-};
-
-git_reference *git_reference__set_name(git_reference *ref, const char *name);
-
-int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags);
-int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid, const git_signature *sig, const char *log_message);
-int git_reference__is_valid_name(const char *refname, unsigned int flags);
-int git_reference__is_branch(const char *ref_name);
-int git_reference__is_remote(const char *ref_name);
-int git_reference__is_tag(const char *ref_name);
-const char *git_reference__shorthand(const char *name);
-
-/**
- * Lookup a reference by name and try to resolve to an OID.
- *
- * You can control how many dereferences this will attempt to resolve the
- * reference with the `max_deref` parameter, or pass -1 to use a sane
- * default. If you pass 0 for `max_deref`, this will not attempt to resolve
- * the reference. For any value of `max_deref` other than 0, not
- * successfully resolving the reference will be reported as an error.
-
- * The generated reference must be freed by the user.
- *
- * @param reference_out Pointer to the looked-up reference
- * @param repo The repository to look up the reference
- * @param name The long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...)
- * @param max_deref Maximum number of dereferences to make of symbolic refs, 0 means simple lookup, < 0 means use default reasonable value
- * @return 0 on success or < 0 on error; not being able to resolve the reference is an error unless 0 was passed for max_deref
- */
-int git_reference_lookup_resolved(
- git_reference **reference_out,
- git_repository *repo,
- const char *name,
- int max_deref);
-
-int git_reference__log_signature(git_signature **out, git_repository *repo);
-
-/** Update a reference after a commit. */
-int git_reference__update_for_commit(
- git_repository *repo,
- git_reference *ref,
- const char *ref_name,
- const git_oid *id,
- const char *operation);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/errors.h"
-
-#include "common.h"
-#include "refspec.h"
-#include "util.h"
-#include "posix.h"
-#include "refs.h"
-#include "vector.h"
-
-int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
-{
- // Ported from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/remote.c#L518-636
-
- size_t llen;
- int is_glob = 0;
- const char *lhs, *rhs;
- int flags;
-
- assert(refspec && input);
-
- memset(refspec, 0x0, sizeof(git_refspec));
- refspec->push = !is_fetch;
-
- lhs = input;
- if (*lhs == '+') {
- refspec->force = 1;
- lhs++;
- }
-
- rhs = strrchr(lhs, ':');
-
- /*
- * Before going on, special case ":" (or "+:") as a refspec
- * for matching refs.
- */
- if (!is_fetch && rhs == lhs && rhs[1] == '\0') {
- refspec->matching = 1;
- refspec->string = git__strdup(input);
- GITERR_CHECK_ALLOC(refspec->string);
- refspec->src = git__strdup("");
- GITERR_CHECK_ALLOC(refspec->src);
- refspec->dst = git__strdup("");
- GITERR_CHECK_ALLOC(refspec->dst);
- return 0;
- }
-
- if (rhs) {
- size_t rlen = strlen(++rhs);
- if (rlen || !is_fetch) {
- is_glob = (1 <= rlen && strchr(rhs, '*'));
- refspec->dst = git__strndup(rhs, rlen);
- }
- }
-
- llen = (rhs ? (size_t)(rhs - lhs - 1) : strlen(lhs));
- if (1 <= llen && memchr(lhs, '*', llen)) {
- if ((rhs && !is_glob) || (!rhs && is_fetch))
- goto invalid;
- is_glob = 1;
- } else if (rhs && is_glob)
- goto invalid;
-
- refspec->pattern = is_glob;
- refspec->src = git__strndup(lhs, llen);
- flags = GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND
- | (is_glob ? GIT_REF_FORMAT_REFSPEC_PATTERN : 0);
-
- if (is_fetch) {
- /*
- * LHS
- * - empty is allowed; it means HEAD.
- * - otherwise it must be a valid looking ref.
- */
- if (!*refspec->src)
- ; /* empty is ok */
- else if (!git_reference__is_valid_name(refspec->src, flags))
- goto invalid;
- /*
- * RHS
- * - missing is ok, and is same as empty.
- * - empty is ok; it means not to store.
- * - otherwise it must be a valid looking ref.
- */
- if (!refspec->dst)
- ; /* ok */
- else if (!*refspec->dst)
- ; /* ok */
- else if (!git_reference__is_valid_name(refspec->dst, flags))
- goto invalid;
- } else {
- /*
- * LHS
- * - empty is allowed; it means delete.
- * - when wildcarded, it must be a valid looking ref.
- * - otherwise, it must be an extended SHA-1, but
- * there is no existing way to validate this.
- */
- if (!*refspec->src)
- ; /* empty is ok */
- else if (is_glob) {
- if (!git_reference__is_valid_name(refspec->src, flags))
- goto invalid;
- }
- else {
- ; /* anything goes, for now */
- }
- /*
- * RHS
- * - missing is allowed, but LHS then must be a
- * valid looking ref.
- * - empty is not allowed.
- * - otherwise it must be a valid looking ref.
- */
- if (!refspec->dst) {
- if (!git_reference__is_valid_name(refspec->src, flags))
- goto invalid;
- } else if (!*refspec->dst) {
- goto invalid;
- } else {
- if (!git_reference__is_valid_name(refspec->dst, flags))
- goto invalid;
- }
-
- /* if the RHS is empty, then it's a copy of the LHS */
- if (!refspec->dst) {
- refspec->dst = git__strdup(refspec->src);
- GITERR_CHECK_ALLOC(refspec->dst);
- }
- }
-
- refspec->string = git__strdup(input);
- GITERR_CHECK_ALLOC(refspec->string);
-
- return 0;
-
- invalid:
- giterr_set(
- GITERR_INVALID,
- "'%s' is not a valid refspec.", input);
- git_refspec__free(refspec);
- return -1;
-}
-
-void git_refspec__free(git_refspec *refspec)
-{
- if (refspec == NULL)
- return;
-
- git__free(refspec->src);
- git__free(refspec->dst);
- git__free(refspec->string);
-
- memset(refspec, 0x0, sizeof(git_refspec));
-}
-
-const char *git_refspec_src(const git_refspec *refspec)
-{
- return refspec == NULL ? NULL : refspec->src;
-}
-
-const char *git_refspec_dst(const git_refspec *refspec)
-{
- return refspec == NULL ? NULL : refspec->dst;
-}
-
-const char *git_refspec_string(const git_refspec *refspec)
-{
- return refspec == NULL ? NULL : refspec->string;
-}
-
-int git_refspec_force(const git_refspec *refspec)
-{
- assert(refspec);
-
- return refspec->force;
-}
-
-int git_refspec_src_matches(const git_refspec *refspec, const char *refname)
-{
- if (refspec == NULL || refspec->src == NULL)
- return false;
-
- return (p_fnmatch(refspec->src, refname, 0) == 0);
-}
-
-int git_refspec_dst_matches(const git_refspec *refspec, const char *refname)
-{
- if (refspec == NULL || refspec->dst == NULL)
- return false;
-
- return (p_fnmatch(refspec->dst, refname, 0) == 0);
-}
-
-static int refspec_transform(
- git_buf *out, const char *from, const char *to, const char *name)
-{
- const char *from_star, *to_star;
- const char *name_slash, *from_slash;
- size_t replacement_len, star_offset;
-
- git_buf_sanitize(out);
- git_buf_clear(out);
-
- /*
- * There are two parts to each side of a refspec, the bit
- * before the star and the bit after it. The star can be in
- * the middle of the pattern, so we need to look at each bit
- * individually.
- */
- from_star = strchr(from, '*');
- to_star = strchr(to, '*');
-
- assert(from_star && to_star);
-
- /* star offset, both in 'from' and in 'name' */
- star_offset = from_star - from;
-
- /* the first half is copied over */
- git_buf_put(out, to, to_star - to);
-
- /* then we copy over the replacement, from the star's offset to the next slash in 'name' */
- name_slash = strchr(name + star_offset, '/');
- if (!name_slash)
- name_slash = strrchr(name, '\0');
-
- /* if there is no slash after the star in 'from', we want to copy everything over */
- from_slash = strchr(from + star_offset, '/');
- if (!from_slash)
- name_slash = strrchr(name, '\0');
-
- replacement_len = (name_slash - name) - star_offset;
- git_buf_put(out, name + star_offset, replacement_len);
-
- return git_buf_puts(out, to_star + 1);
-}
-
-int git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name)
-{
- assert(out && spec && name);
- git_buf_sanitize(out);
-
- if (!git_refspec_src_matches(spec, name)) {
- giterr_set(GITERR_INVALID, "ref '%s' doesn't match the source", name);
- return -1;
- }
-
- if (!spec->pattern)
- return git_buf_puts(out, spec->dst);
-
- return refspec_transform(out, spec->src, spec->dst, name);
-}
-
-int git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name)
-{
- assert(out && spec && name);
- git_buf_sanitize(out);
-
- if (!git_refspec_dst_matches(spec, name)) {
- giterr_set(GITERR_INVALID, "ref '%s' doesn't match the destination", name);
- return -1;
- }
-
- if (!spec->pattern)
- return git_buf_puts(out, spec->src);
-
- return refspec_transform(out, spec->dst, spec->src, name);
-}
-
-int git_refspec__serialize(git_buf *out, const git_refspec *refspec)
-{
- if (refspec->force)
- git_buf_putc(out, '+');
-
- git_buf_printf(out, "%s:%s",
- refspec->src != NULL ? refspec->src : "",
- refspec->dst != NULL ? refspec->dst : "");
-
- return git_buf_oom(out) == false;
-}
-
-int git_refspec_is_wildcard(const git_refspec *spec)
-{
- assert(spec && spec->src);
-
- return (spec->src[strlen(spec->src) - 1] == '*');
-}
-
-git_direction git_refspec_direction(const git_refspec *spec)
-{
- assert(spec);
-
- return spec->push;
-}
-
-int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs)
-{
- git_buf buf = GIT_BUF_INIT;
- size_t j, pos;
- git_remote_head key;
-
- const char* formatters[] = {
- GIT_REFS_DIR "%s",
- GIT_REFS_TAGS_DIR "%s",
- GIT_REFS_HEADS_DIR "%s",
- NULL
- };
-
- git_refspec *cur = git__calloc(1, sizeof(git_refspec));
- GITERR_CHECK_ALLOC(cur);
-
- cur->force = spec->force;
- cur->push = spec->push;
- cur->pattern = spec->pattern;
- cur->matching = spec->matching;
- cur->string = git__strdup(spec->string);
-
- /* shorthand on the lhs */
- if (git__prefixcmp(spec->src, GIT_REFS_DIR)) {
- for (j = 0; formatters[j]; j++) {
- git_buf_clear(&buf);
- git_buf_printf(&buf, formatters[j], spec->src);
- GITERR_CHECK_ALLOC_BUF(&buf);
-
- key.name = (char *) git_buf_cstr(&buf);
- if (!git_vector_search(&pos, refs, &key)) {
- /* we found something to match the shorthand, set src to that */
- cur->src = git_buf_detach(&buf);
- }
- }
- }
-
- /* No shorthands found, copy over the name */
- if (cur->src == NULL && spec->src != NULL) {
- cur->src = git__strdup(spec->src);
- GITERR_CHECK_ALLOC(cur->src);
- }
-
- if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) {
- /* if it starts with "remotes" then we just prepend "refs/" */
- if (!git__prefixcmp(spec->dst, "remotes/")) {
- git_buf_puts(&buf, GIT_REFS_DIR);
- } else {
- git_buf_puts(&buf, GIT_REFS_HEADS_DIR);
- }
-
- git_buf_puts(&buf, spec->dst);
- GITERR_CHECK_ALLOC_BUF(&buf);
-
- cur->dst = git_buf_detach(&buf);
- }
-
- git_buf_free(&buf);
-
- if (cur->dst == NULL && spec->dst != NULL) {
- cur->dst = git__strdup(spec->dst);
- GITERR_CHECK_ALLOC(cur->dst);
- }
-
- return git_vector_insert(out, cur);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_refspec_h__
-#define INCLUDE_refspec_h__
-
-#include "git2/refspec.h"
-#include "buffer.h"
-#include "vector.h"
-
-struct git_refspec {
- char *string;
- char *src;
- char *dst;
- unsigned int force :1,
- push : 1,
- pattern :1,
- matching :1;
-};
-
-#define GIT_REFSPEC_TAGS "refs/tags/*:refs/tags/*"
-
-int git_refspec__parse(
- struct git_refspec *refspec,
- const char *str,
- bool is_fetch);
-
-void git_refspec__free(git_refspec *refspec);
-
-int git_refspec__serialize(git_buf *out, const git_refspec *refspec);
-
-/**
- * Determines if a refspec is a wildcard refspec.
- *
- * @param spec the refspec
- * @return 1 if the refspec is a wildcard, 0 otherwise
- */
-int git_refspec_is_wildcard(const git_refspec *spec);
-
-/**
- * DWIM `spec` with `refs` existing on the remote, append the dwim'ed
- * result in `out`.
- */
-int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/config.h"
-#include "git2/types.h"
-#include "git2/oid.h"
-#include "git2/net.h"
-
-#include "common.h"
-#include "config.h"
-#include "repository.h"
-#include "remote.h"
-#include "fetch.h"
-#include "refs.h"
-#include "refspec.h"
-#include "fetchhead.h"
-#include "push.h"
-
-#define CONFIG_URL_FMT "remote.%s.url"
-#define CONFIG_PUSHURL_FMT "remote.%s.pushurl"
-#define CONFIG_FETCH_FMT "remote.%s.fetch"
-#define CONFIG_PUSH_FMT "remote.%s.push"
-#define CONFIG_TAGOPT_FMT "remote.%s.tagopt"
-
-static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
-static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name);
-char *apply_insteadof(git_config *config, const char *url, int direction);
-
-static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch)
-{
- git_refspec *spec;
-
- spec = git__calloc(1, sizeof(git_refspec));
- GITERR_CHECK_ALLOC(spec);
-
- if (git_refspec__parse(spec, string, is_fetch) < 0) {
- git__free(spec);
- return -1;
- }
-
- spec->push = !is_fetch;
- if (git_vector_insert(vector, spec) < 0) {
- git_refspec__free(spec);
- git__free(spec);
- return -1;
- }
-
- return 0;
-}
-
-static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
-{
- return add_refspec_to(&remote->refspecs, string, is_fetch);
-}
-
-static int download_tags_value(git_remote *remote, git_config *cfg)
-{
- git_config_entry *ce;
- git_buf buf = GIT_BUF_INIT;
- int error;
-
- if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
- return -1;
-
- error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
- git_buf_free(&buf);
-
- if (!error && ce && ce->value) {
- if (!strcmp(ce->value, "--no-tags"))
- remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
- else if (!strcmp(ce->value, "--tags"))
- remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
- }
-
- git_config_entry_free(ce);
- return error;
-}
-
-static int ensure_remote_name_is_valid(const char *name)
-{
- int error = 0;
-
- if (!git_remote_is_valid_name(name)) {
- giterr_set(
- GITERR_CONFIG,
- "'%s' is not a valid remote name.", name ? name : "(null)");
- error = GIT_EINVALIDSPEC;
- }
-
- return error;
-}
-
-static int write_add_refspec(git_repository *repo, const char *name, const char *refspec, bool fetch)
-{
- git_config *cfg;
- git_buf var = GIT_BUF_INIT;
- git_refspec spec;
- const char *fmt;
- int error;
-
- if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
- return error;
-
- fmt = fetch ? CONFIG_FETCH_FMT : CONFIG_PUSH_FMT;
-
- if ((error = ensure_remote_name_is_valid(name)) < 0)
- return error;
-
- if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0) {
- if (giterr_last()->klass != GITERR_NOMEMORY)
- error = GIT_EINVALIDSPEC;
-
- return error;
- }
-
- git_refspec__free(&spec);
-
- if ((error = git_buf_printf(&var, fmt, name)) < 0)
- return error;
-
- /*
- * "$^" is a unmatcheable regexp: it will not match anything at all, so
- * all values will be considered new and we will not replace any
- * present value.
- */
- if ((error = git_config_set_multivar(cfg, var.ptr, "$^", refspec)) < 0) {
- goto cleanup;
- }
-
-cleanup:
- git_buf_free(&var);
- return 0;
-}
-
-#if 0
-/* We could export this as a helper */
-static int get_check_cert(int *out, git_repository *repo)
-{
- git_config *cfg;
- const char *val;
- int error = 0;
-
- assert(out && repo);
-
- /* By default, we *DO* want to verify the certificate. */
- *out = 1;
-
- /* Go through the possible sources for SSL verification settings, from
- * most specific to least specific. */
-
- /* GIT_SSL_NO_VERIFY environment variable */
- if ((val = p_getenv("GIT_SSL_NO_VERIFY")) != NULL)
- return git_config_parse_bool(out, val);
-
- /* http.sslVerify config setting */
- if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
- return error;
-
- *out = git_config__get_bool_force(cfg, "http.sslverify", 1);
- return 0;
-}
-#endif
-
-static int canonicalize_url(git_buf *out, const char *in)
-{
- if (in == NULL || strlen(in) == 0) {
- giterr_set(GITERR_INVALID, "cannot set empty URL");
- return GIT_EINVALIDSPEC;
- }
-
-#ifdef GIT_WIN32
- /* Given a UNC path like \\server\path, we need to convert this
- * to //server/path for compatibility with core git.
- */
- if (in[0] == '\\' && in[1] == '\\' &&
- (git__isalpha(in[2]) || git__isdigit(in[2]))) {
- const char *c;
- for (c = in; *c; c++)
- git_buf_putc(out, *c == '\\' ? '/' : *c);
-
- return git_buf_oom(out) ? -1 : 0;
- }
-#endif
-
- return git_buf_puts(out, in);
-}
-
-static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
-{
- git_remote *remote;
- git_config *config = NULL;
- git_buf canonical_url = GIT_BUF_INIT;
- git_buf var = GIT_BUF_INIT;
- int error = -1;
-
- /* name is optional */
- assert(out && repo && url);
-
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
- return error;
-
- remote = git__calloc(1, sizeof(git_remote));
- GITERR_CHECK_ALLOC(remote);
-
- remote->repo = repo;
-
- if ((error = git_vector_init(&remote->refs, 32, NULL)) < 0 ||
- (error = canonicalize_url(&canonical_url, url)) < 0)
- goto on_error;
-
- remote->url = apply_insteadof(repo->_config, canonical_url.ptr, GIT_DIRECTION_FETCH);
-
- if (name != NULL) {
- remote->name = git__strdup(name);
- GITERR_CHECK_ALLOC(remote->name);
-
- if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0)
- goto on_error;
-
- if ((error = git_config_set_string(config, var.ptr, canonical_url.ptr)) < 0)
- goto on_error;
- }
-
- if (fetch != NULL) {
- if ((error = add_refspec(remote, fetch, true)) < 0)
- goto on_error;
-
- /* only write for non-anonymous remotes */
- if (name && (error = write_add_refspec(repo, name, fetch, true)) < 0)
- goto on_error;
-
- if ((error = git_repository_config_snapshot(&config, repo)) < 0)
- goto on_error;
-
- if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
- goto on_error;
-
- /* Move the data over to where the matching functions can find them */
- if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
- goto on_error;
- }
-
- /* A remote without a name doesn't download tags */
- if (!name)
- remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
- else
- remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
-
-
- git_buf_free(&var);
-
- *out = remote;
- error = 0;
-
-on_error:
- if (error)
- git_remote_free(remote);
-
- git_config_free(config);
- git_buf_free(&canonical_url);
- git_buf_free(&var);
- return error;
-}
-
-static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
-{
- int error;
- git_remote *remote;
-
- error = git_remote_lookup(&remote, repo, name);
-
- if (error == GIT_ENOTFOUND)
- return 0;
-
- if (error < 0)
- return error;
-
- git_remote_free(remote);
-
- giterr_set(
- GITERR_CONFIG,
- "Remote '%s' already exists.", name);
-
- return GIT_EEXISTS;
-}
-
-
-int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
-{
- git_buf buf = GIT_BUF_INIT;
- int error;
-
- if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0)
- return -1;
-
- error = git_remote_create_with_fetchspec(out, repo, name, url, git_buf_cstr(&buf));
- git_buf_free(&buf);
-
- return error;
-}
-
-int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
-{
- git_remote *remote = NULL;
- int error;
-
- if ((error = ensure_remote_name_is_valid(name)) < 0)
- return error;
-
- if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
- return error;
-
- if (create_internal(&remote, repo, name, url, fetch) < 0)
- goto on_error;
-
- *out = remote;
-
- return 0;
-
-on_error:
- git_remote_free(remote);
- return -1;
-}
-
-int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url)
-{
- return create_internal(out, repo, NULL, url, NULL);
-}
-
-int git_remote_dup(git_remote **dest, git_remote *source)
-{
- size_t i;
- int error = 0;
- git_refspec *spec;
- git_remote *remote = git__calloc(1, sizeof(git_remote));
- GITERR_CHECK_ALLOC(remote);
-
- if (source->name != NULL) {
- remote->name = git__strdup(source->name);
- GITERR_CHECK_ALLOC(remote->name);
- }
-
- if (source->url != NULL) {
- remote->url = git__strdup(source->url);
- GITERR_CHECK_ALLOC(remote->url);
- }
-
- if (source->pushurl != NULL) {
- remote->pushurl = git__strdup(source->pushurl);
- GITERR_CHECK_ALLOC(remote->pushurl);
- }
-
- remote->repo = source->repo;
- remote->download_tags = source->download_tags;
- remote->prune_refs = source->prune_refs;
-
- if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
- git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
- git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
- error = -1;
- goto cleanup;
- }
-
- git_vector_foreach(&source->refspecs, i, spec) {
- if ((error = add_refspec(remote, spec->string, !spec->push)) < 0)
- goto cleanup;
- }
-
- *dest = remote;
-
-cleanup:
-
- if (error < 0)
- git__free(remote);
-
- return error;
-}
-
-struct refspec_cb_data {
- git_remote *remote;
- int fetch;
-};
-
-static int refspec_cb(const git_config_entry *entry, void *payload)
-{
- struct refspec_cb_data *data = (struct refspec_cb_data *)payload;
- return add_refspec(data->remote, entry->value, data->fetch);
-}
-
-static int get_optional_config(
- bool *found, git_config *config, git_buf *buf,
- git_config_foreach_cb cb, void *payload)
-{
- int error = 0;
- const char *key = git_buf_cstr(buf);
-
- if (git_buf_oom(buf))
- return -1;
-
- if (cb != NULL)
- error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
- else
- error = git_config_get_string(payload, config, key);
-
- if (found)
- *found = !error;
-
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
-
- return error;
-}
-
-int git_remote_lookup(git_remote **out, git_repository *repo, const char *name)
-{
- git_remote *remote;
- git_buf buf = GIT_BUF_INIT;
- const char *val;
- int error = 0;
- git_config *config;
- struct refspec_cb_data data = { NULL };
- bool optional_setting_found = false, found;
-
- assert(out && repo && name);
-
- if ((error = ensure_remote_name_is_valid(name)) < 0)
- return error;
-
- if ((error = git_repository_config_snapshot(&config, repo)) < 0)
- return error;
-
- remote = git__calloc(1, sizeof(git_remote));
- GITERR_CHECK_ALLOC(remote);
-
- remote->name = git__strdup(name);
- GITERR_CHECK_ALLOC(remote->name);
-
- if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
- git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
- git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 ||
- git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
- error = -1;
- goto cleanup;
- }
-
- if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0)
- goto cleanup;
-
- if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
- goto cleanup;
-
- optional_setting_found |= found;
-
- remote->repo = repo;
- remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
-
- if (found && strlen(val) > 0) {
- remote->url = apply_insteadof(config, val, GIT_DIRECTION_FETCH);
- GITERR_CHECK_ALLOC(remote->url);
- }
-
- val = NULL;
- git_buf_clear(&buf);
- git_buf_printf(&buf, "remote.%s.pushurl", name);
-
- if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
- goto cleanup;
-
- optional_setting_found |= found;
-
- if (!optional_setting_found) {
- error = GIT_ENOTFOUND;
- giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name);
- goto cleanup;
- }
-
- if (found && strlen(val) > 0) {
- remote->pushurl = apply_insteadof(config, val, GIT_DIRECTION_PUSH);
- GITERR_CHECK_ALLOC(remote->pushurl);
- }
-
- data.remote = remote;
- data.fetch = true;
-
- git_buf_clear(&buf);
- git_buf_printf(&buf, "remote.%s.fetch", name);
-
- if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
- goto cleanup;
-
- data.fetch = false;
- git_buf_clear(&buf);
- git_buf_printf(&buf, "remote.%s.push", name);
-
- if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
- goto cleanup;
-
- if (download_tags_value(remote, config) < 0)
- goto cleanup;
-
- if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
- goto cleanup;
-
- /* Move the data over to where the matching functions can find them */
- if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
- goto cleanup;
-
- *out = remote;
-
-cleanup:
- git_config_free(config);
- git_buf_free(&buf);
-
- if (error < 0)
- git_remote_free(remote);
-
- return error;
-}
-
-static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name)
-{
- git_buf buf = GIT_BUF_INIT;
- int error = 0;
-
- git_buf_printf(&buf, "remote.%s.prune", name);
-
- if ((error = git_config_get_bool(&remote->prune_refs, config, git_buf_cstr(&buf))) < 0) {
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
-
- if ((error = git_config_get_bool(&remote->prune_refs, config, "fetch.prune")) < 0) {
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
- }
- }
- }
-
- git_buf_free(&buf);
- return error;
-}
-
-const char *git_remote_name(const git_remote *remote)
-{
- assert(remote);
- return remote->name;
-}
-
-git_repository *git_remote_owner(const git_remote *remote)
-{
- assert(remote);
- return remote->repo;
-}
-
-const char *git_remote_url(const git_remote *remote)
-{
- assert(remote);
- return remote->url;
-}
-
-static int set_url(git_repository *repo, const char *remote, const char *pattern, const char *url)
-{
- git_config *cfg;
- git_buf buf = GIT_BUF_INIT, canonical_url = GIT_BUF_INIT;
- int error;
-
- assert(repo && remote);
-
- if ((error = ensure_remote_name_is_valid(remote)) < 0)
- return error;
-
- if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
- return error;
-
- if ((error = git_buf_printf(&buf, pattern, remote)) < 0)
- return error;
-
- if (url) {
- if ((error = canonicalize_url(&canonical_url, url)) < 0)
- goto cleanup;
-
- error = git_config_set_string(cfg, buf.ptr, url);
- } else {
- error = git_config_delete_entry(cfg, buf.ptr);
- }
-
-cleanup:
- git_buf_free(&canonical_url);
- git_buf_free(&buf);
-
- return error;
-}
-
-int git_remote_set_url(git_repository *repo, const char *remote, const char *url)
-{
- return set_url(repo, remote, CONFIG_URL_FMT, url);
-}
-
-const char *git_remote_pushurl(const git_remote *remote)
-{
- assert(remote);
- return remote->pushurl;
-}
-
-int git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url)
-{
- return set_url(repo, remote, CONFIG_PUSHURL_FMT, url);
-}
-
-const char* git_remote__urlfordirection(git_remote *remote, int direction)
-{
- assert(remote);
-
- assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
-
- if (direction == GIT_DIRECTION_FETCH) {
- return remote->url;
- }
-
- if (direction == GIT_DIRECTION_PUSH) {
- return remote->pushurl ? remote->pushurl : remote->url;
- }
-
- return NULL;
-}
-
-int set_transport_callbacks(git_transport *t, const git_remote_callbacks *cbs)
-{
- if (!t->set_callbacks || !cbs)
- return 0;
-
- return t->set_callbacks(t, cbs->sideband_progress, NULL,
- cbs->certificate_check, cbs->payload);
-}
-
-static int set_transport_custom_headers(git_transport *t, const git_strarray *custom_headers)
-{
- if (!t->set_custom_headers)
- return 0;
-
- return t->set_custom_headers(t, custom_headers);
-}
-
-int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy, const git_strarray *custom_headers)
-{
- git_transport *t;
- const char *url;
- int flags = GIT_TRANSPORTFLAGS_NONE;
- int error;
- void *payload = NULL;
- git_cred_acquire_cb credentials = NULL;
- git_transport_cb transport = NULL;
-
- assert(remote);
-
- if (callbacks) {
- GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
- credentials = callbacks->credentials;
- transport = callbacks->transport;
- payload = callbacks->payload;
- }
-
- if (proxy)
- GITERR_CHECK_VERSION(proxy, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
-
- t = remote->transport;
-
- url = git_remote__urlfordirection(remote, direction);
- if (url == NULL) {
- giterr_set(GITERR_INVALID,
- "Malformed remote '%s' - missing URL", remote->name);
- return -1;
- }
-
- /* If we don't have a transport object yet, and the caller specified a
- * custom transport factory, use that */
- if (!t && transport &&
- (error = transport(&t, remote, payload)) < 0)
- return error;
-
- /* If we still don't have a transport, then use the global
- * transport registrations which map URI schemes to transport factories */
- if (!t && (error = git_transport_new(&t, remote, url)) < 0)
- return error;
-
- if ((error = set_transport_custom_headers(t, custom_headers)) != 0)
- goto on_error;
-
- if ((error = set_transport_callbacks(t, callbacks)) < 0 ||
- (error = t->connect(t, url, credentials, payload, proxy, direction, flags)) != 0)
- goto on_error;
-
- remote->transport = t;
-
- return 0;
-
-on_error:
- t->free(t);
-
- if (t == remote->transport)
- remote->transport = NULL;
-
- return error;
-}
-
-int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
-{
- assert(remote);
-
- if (!remote->transport) {
- giterr_set(GITERR_NET, "this remote has never connected");
- return -1;
- }
-
- return remote->transport->ls(out, size, remote->transport);
-}
-
-int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
-{
- git_config *cfg;
- git_config_entry *ce = NULL;
- git_buf val = GIT_BUF_INIT;
- int error;
-
- assert(remote);
-
- if (!proxy_url || !remote->repo)
- return -1;
-
- *proxy_url = NULL;
-
- if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
- return error;
-
- /* Go through the possible sources for proxy configuration, from most specific
- * to least specific. */
-
- /* remote.<name>.proxy config setting */
- if (remote->name && remote->name[0]) {
- git_buf buf = GIT_BUF_INIT;
-
- if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0)
- return error;
-
- error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
- git_buf_free(&buf);
-
- if (error < 0)
- return error;
-
- if (ce && ce->value) {
- *proxy_url = git__strdup(ce->value);
- goto found;
- }
- }
-
- /* http.proxy config setting */
- if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0)
- return error;
-
- if (ce && ce->value) {
- *proxy_url = git__strdup(ce->value);
- goto found;
- }
-
- /* HTTP_PROXY / HTTPS_PROXY environment variables */
- error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
-
- if (error < 0) {
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
-
- return error;
- }
-
- *proxy_url = git_buf_detach(&val);
-
-found:
- GITERR_CHECK_ALLOC(*proxy_url);
- git_config_entry_free(ce);
-
- return 0;
-}
-
-/* DWIM `refspecs` based on `refs` and append the output to `out` */
-static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
-{
- size_t i;
- git_refspec *spec;
-
- git_vector_foreach(refspecs, i, spec) {
- if (git_refspec__dwim_one(out, spec, refs) < 0)
- return -1;
- }
-
- return 0;
-}
-
-static void free_refspecs(git_vector *vec)
-{
- size_t i;
- git_refspec *spec;
-
- git_vector_foreach(vec, i, spec) {
- git_refspec__free(spec);
- git__free(spec);
- }
-
- git_vector_clear(vec);
-}
-
-static int remote_head_cmp(const void *_a, const void *_b)
-{
- const git_remote_head *a = (git_remote_head *) _a;
- const git_remote_head *b = (git_remote_head *) _b;
-
- return git__strcmp_cb(a->name, b->name);
-}
-
-static int ls_to_vector(git_vector *out, git_remote *remote)
-{
- git_remote_head **heads;
- size_t heads_len, i;
-
- if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
- return -1;
-
- if (git_vector_init(out, heads_len, remote_head_cmp) < 0)
- return -1;
-
- for (i = 0; i < heads_len; i++) {
- if (git_vector_insert(out, heads[i]) < 0)
- return -1;
- }
-
- return 0;
-}
-
-int git_remote_download(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts)
-{
- int error = -1;
- size_t i;
- git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
- const git_remote_callbacks *cbs = NULL;
- const git_strarray *custom_headers = NULL;
- const git_proxy_options *proxy = NULL;
-
- assert(remote);
-
- if (opts) {
- GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
- cbs = &opts->callbacks;
- custom_headers = &opts->custom_headers;
- GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
- proxy = &opts->proxy_opts;
- }
-
- if (!git_remote_connected(remote) &&
- (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) < 0)
- goto on_error;
-
- if (ls_to_vector(&refs, remote) < 0)
- return -1;
-
- if ((git_vector_init(&specs, 0, NULL)) < 0)
- goto on_error;
-
- remote->passed_refspecs = 0;
- if (!refspecs || !refspecs->count) {
- to_active = &remote->refspecs;
- } else {
- for (i = 0; i < refspecs->count; i++) {
- if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0)
- goto on_error;
- }
-
- to_active = &specs;
- remote->passed_refspecs = 1;
- }
-
- free_refspecs(&remote->passive_refspecs);
- if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0)
- goto on_error;
-
- free_refspecs(&remote->active_refspecs);
- error = dwim_refspecs(&remote->active_refspecs, to_active, &refs);
-
- git_vector_free(&refs);
- free_refspecs(&specs);
- git_vector_free(&specs);
-
- if (error < 0)
- return error;
-
- if (remote->push) {
- git_push_free(remote->push);
- remote->push = NULL;
- }
-
- if ((error = git_fetch_negotiate(remote, opts)) < 0)
- return error;
-
- return git_fetch_download_pack(remote, cbs);
-
-on_error:
- git_vector_free(&refs);
- free_refspecs(&specs);
- git_vector_free(&specs);
- return error;
-}
-
-int git_remote_fetch(
- git_remote *remote,
- const git_strarray *refspecs,
- const git_fetch_options *opts,
- const char *reflog_message)
-{
- int error, update_fetchhead = 1;
- git_remote_autotag_option_t tagopt = remote->download_tags;
- bool prune = false;
- git_buf reflog_msg_buf = GIT_BUF_INIT;
- const git_remote_callbacks *cbs = NULL;
- const git_strarray *custom_headers = NULL;
- const git_proxy_options *proxy = NULL;
-
- if (opts) {
- GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
- cbs = &opts->callbacks;
- custom_headers = &opts->custom_headers;
- update_fetchhead = opts->update_fetchhead;
- tagopt = opts->download_tags;
- GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
- proxy = &opts->proxy_opts;
- }
-
- /* Connect and download everything */
- if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) != 0)
- return error;
-
- error = git_remote_download(remote, refspecs, opts);
-
- /* We don't need to be connected anymore */
- git_remote_disconnect(remote);
-
- /* If the download failed, return the error */
- if (error != 0)
- return error;
-
- /* Default reflog message */
- if (reflog_message)
- git_buf_sets(&reflog_msg_buf, reflog_message);
- else {
- git_buf_printf(&reflog_msg_buf, "fetch %s",
- remote->name ? remote->name : remote->url);
- }
-
- /* Create "remote/foo" branches for all remote branches */
- error = git_remote_update_tips(remote, cbs, update_fetchhead, tagopt, git_buf_cstr(&reflog_msg_buf));
- git_buf_free(&reflog_msg_buf);
- if (error < 0)
- return error;
-
- if (opts && opts->prune == GIT_FETCH_PRUNE)
- prune = true;
- else if (opts && opts->prune == GIT_FETCH_PRUNE_UNSPECIFIED && remote->prune_refs)
- prune = true;
- else if (opts && opts->prune == GIT_FETCH_NO_PRUNE)
- prune = false;
- else
- prune = remote->prune_refs;
-
- if (prune)
- error = git_remote_prune(remote, cbs);
-
- return error;
-}
-
-static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
-{
- unsigned int i;
- git_remote_head *remote_ref;
-
- assert(update_heads && fetchspec_src);
-
- *out = NULL;
-
- git_vector_foreach(update_heads, i, remote_ref) {
- if (strcmp(remote_ref->name, fetchspec_src) == 0) {
- *out = remote_ref;
- break;
- }
- }
-
- return 0;
-}
-
-static int ref_to_update(int *update, git_buf *remote_name, git_remote *remote, git_refspec *spec, const char *ref_name)
-{
- int error = 0;
- git_repository *repo;
- git_buf upstream_remote = GIT_BUF_INIT;
- git_buf upstream_name = GIT_BUF_INIT;
-
- repo = git_remote_owner(remote);
-
- if ((!git_reference__is_branch(ref_name)) ||
- !git_remote_name(remote) ||
- (error = git_branch_upstream_remote(&upstream_remote, repo, ref_name) < 0) ||
- git__strcmp(git_remote_name(remote), git_buf_cstr(&upstream_remote)) ||
- (error = git_branch_upstream_name(&upstream_name, repo, ref_name)) < 0 ||
- !git_refspec_dst_matches(spec, git_buf_cstr(&upstream_name)) ||
- (error = git_refspec_rtransform(remote_name, spec, upstream_name.ptr)) < 0) {
- /* Not an error if there is no upstream */
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
-
- *update = 0;
- } else {
- *update = 1;
- }
-
- git_buf_free(&upstream_remote);
- git_buf_free(&upstream_name);
- return error;
-}
-
-static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_refspec *spec, git_vector *update_heads, git_reference *ref)
-{
- git_reference *resolved_ref = NULL;
- git_buf remote_name = GIT_BUF_INIT;
- git_config *config = NULL;
- const char *ref_name;
- int error = 0, update;
-
- assert(out && spec && ref);
-
- *out = NULL;
-
- error = git_reference_resolve(&resolved_ref, ref);
-
- /* If we're in an unborn branch, let's pretend nothing happened */
- if (error == GIT_ENOTFOUND && git_reference_type(ref) == GIT_REF_SYMBOLIC) {
- ref_name = git_reference_symbolic_target(ref);
- error = 0;
- } else {
- ref_name = git_reference_name(resolved_ref);
- }
-
- if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0)
- goto cleanup;
-
- if (update)
- error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name));
-
-cleanup:
- git_buf_free(&remote_name);
- git_reference_free(resolved_ref);
- git_config_free(config);
- return error;
-}
-
-static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads)
-{
- git_reference *head_ref = NULL;
- git_fetchhead_ref *fetchhead_ref;
- git_remote_head *remote_ref, *merge_remote_ref;
- git_vector fetchhead_refs;
- bool include_all_fetchheads;
- unsigned int i = 0;
- int error = 0;
-
- assert(remote);
-
- /* no heads, nothing to do */
- if (update_heads->length == 0)
- return 0;
-
- if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0)
- return -1;
-
- /* Iff refspec is * (but not subdir slash star), include tags */
- include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0);
-
- /* Determine what to merge: if refspec was a wildcard, just use HEAD */
- if (git_refspec_is_wildcard(spec)) {
- if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 ||
- (error = remote_head_for_ref(&merge_remote_ref, remote, spec, update_heads, head_ref)) < 0)
- goto cleanup;
- } else {
- /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */
- if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0)
- goto cleanup;
- }
-
- /* Create the FETCH_HEAD file */
- git_vector_foreach(update_heads, i, remote_ref) {
- int merge_this_fetchhead = (merge_remote_ref == remote_ref);
-
- if (!include_all_fetchheads &&
- !git_refspec_src_matches(spec, remote_ref->name) &&
- !merge_this_fetchhead)
- continue;
-
- if (git_fetchhead_ref_create(&fetchhead_ref,
- &remote_ref->oid,
- merge_this_fetchhead,
- remote_ref->name,
- git_remote_url(remote)) < 0)
- goto cleanup;
-
- if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0)
- goto cleanup;
- }
-
- git_fetchhead_write(remote->repo, &fetchhead_refs);
-
-cleanup:
- for (i = 0; i < fetchhead_refs.length; ++i)
- git_fetchhead_ref_free(fetchhead_refs.contents[i]);
-
- git_vector_free(&fetchhead_refs);
- git_reference_free(head_ref);
-
- return error;
-}
-
-/**
- * Generate a list of candidates for pruning by getting a list of
- * references which match the rhs of an active refspec.
- */
-static int prune_candidates(git_vector *candidates, git_remote *remote)
-{
- git_strarray arr = { 0 };
- size_t i;
- int error;
-
- if ((error = git_reference_list(&arr, remote->repo)) < 0)
- return error;
-
- for (i = 0; i < arr.count; i++) {
- const char *refname = arr.strings[i];
- char *refname_dup;
-
- if (!git_remote__matching_dst_refspec(remote, refname))
- continue;
-
- refname_dup = git__strdup(refname);
- GITERR_CHECK_ALLOC(refname_dup);
-
- if ((error = git_vector_insert(candidates, refname_dup)) < 0)
- goto out;
- }
-
-out:
- git_strarray_free(&arr);
- return error;
-}
-
-static int find_head(const void *_a, const void *_b)
-{
- git_remote_head *a = (git_remote_head *) _a;
- git_remote_head *b = (git_remote_head *) _b;
-
- return strcmp(a->name, b->name);
-}
-
-int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks)
-{
- size_t i, j;
- git_vector remote_refs = GIT_VECTOR_INIT;
- git_vector candidates = GIT_VECTOR_INIT;
- const git_refspec *spec;
- const char *refname;
- int error;
- git_oid zero_id = {{ 0 }};
-
- if (callbacks)
- GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
-
- if ((error = ls_to_vector(&remote_refs, remote)) < 0)
- goto cleanup;
-
- git_vector_set_cmp(&remote_refs, find_head);
-
- if ((error = prune_candidates(&candidates, remote)) < 0)
- goto cleanup;
-
- /*
- * Remove those entries from the candidate list for which we
- * can find a remote reference in at least one refspec.
- */
- git_vector_foreach(&candidates, i, refname) {
- git_vector_foreach(&remote->active_refspecs, j, spec) {
- git_buf buf = GIT_BUF_INIT;
- size_t pos;
- char *src_name;
- git_remote_head key = {0};
-
- if (!git_refspec_dst_matches(spec, refname))
- continue;
-
- if ((error = git_refspec_rtransform(&buf, spec, refname)) < 0)
- goto cleanup;
-
- key.name = (char *) git_buf_cstr(&buf);
- error = git_vector_search(&pos, &remote_refs, &key);
- git_buf_free(&buf);
-
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- if (error == GIT_ENOTFOUND)
- continue;
-
- /* if we did find a source, remove it from the candiates */
- if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0)
- goto cleanup;
-
- git__free(src_name);
- break;
- }
- }
-
- /*
- * For those candidates still left in the list, we need to
- * remove them. We do not remove symrefs, as those are for
- * stuff like origin/HEAD which will never match, but we do
- * not want to remove them.
- */
- git_vector_foreach(&candidates, i, refname) {
- git_reference *ref;
- git_oid id;
-
- if (refname == NULL)
- continue;
-
- error = git_reference_lookup(&ref, remote->repo, refname);
- /* as we want it gone, let's not consider this an error */
- if (error == GIT_ENOTFOUND)
- continue;
-
- if (error < 0)
- goto cleanup;
-
- if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
- git_reference_free(ref);
- continue;
- }
-
- git_oid_cpy(&id, git_reference_target(ref));
- error = git_reference_delete(ref);
- git_reference_free(ref);
- if (error < 0)
- goto cleanup;
-
- if (callbacks && callbacks->update_tips)
- error = callbacks->update_tips(refname, &id, &zero_id, callbacks->payload);
-
- if (error < 0)
- goto cleanup;
- }
-
-cleanup:
- git_vector_free(&remote_refs);
- git_vector_free_deep(&candidates);
- return error;
-}
-
-static int update_tips_for_spec(
- git_remote *remote,
- const git_remote_callbacks *callbacks,
- int update_fetchhead,
- git_remote_autotag_option_t tagopt,
- git_refspec *spec,
- git_vector *refs,
- const char *log_message)
-{
- int error = 0, autotag;
- unsigned int i = 0;
- git_buf refname = GIT_BUF_INIT;
- git_oid old;
- git_odb *odb;
- git_remote_head *head;
- git_reference *ref;
- git_refspec tagspec;
- git_vector update_heads;
-
- assert(remote);
-
- if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
- return -1;
-
- if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
- return -1;
-
- /* Make a copy of the transport's refs */
- if (git_vector_init(&update_heads, 16, NULL) < 0)
- return -1;
-
- for (; i < refs->length; ++i) {
- head = git_vector_get(refs, i);
- autotag = 0;
- git_buf_clear(&refname);
-
- /* Ignore malformed ref names (which also saves us from tag^{} */
- if (!git_reference_is_valid_name(head->name))
- continue;
-
- /* If we have a tag, see if the auto-follow rules say to update it */
- if (git_refspec_src_matches(&tagspec, head->name)) {
- if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
-
- if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_AUTO)
- autotag = 1;
-
- git_buf_clear(&refname);
- if (git_buf_puts(&refname, head->name) < 0)
- goto on_error;
- }
- }
-
- /* If we didn't want to auto-follow the tag, check if the refspec matches */
- if (!autotag && git_refspec_src_matches(spec, head->name)) {
- if (spec->dst) {
- if (git_refspec_transform(&refname, spec, head->name) < 0)
- goto on_error;
- } else {
- /*
- * no rhs mans store it in FETCH_HEAD, even if we don't
- update anything else.
- */
- if ((error = git_vector_insert(&update_heads, head)) < 0)
- goto on_error;
-
- continue;
- }
- }
-
- /* If we still don't have a refname, we don't want it */
- if (git_buf_len(&refname) == 0) {
- continue;
- }
-
- /* In autotag mode, only create tags for objects already in db */
- if (autotag && !git_odb_exists(odb, &head->oid))
- continue;
-
- if (!autotag && git_vector_insert(&update_heads, head) < 0)
- goto on_error;
-
- error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
- if (error < 0 && error != GIT_ENOTFOUND)
- goto on_error;
-
- if (error == GIT_ENOTFOUND) {
- memset(&old, 0, GIT_OID_RAWSZ);
-
- if (autotag && git_vector_insert(&update_heads, head) < 0)
- goto on_error;
- }
-
- if (!git_oid__cmp(&old, &head->oid))
- continue;
-
- /* In autotag mode, don't overwrite any locally-existing tags */
- error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag,
- log_message);
-
- if (error == GIT_EEXISTS)
- continue;
-
- if (error < 0)
- goto on_error;
-
- git_reference_free(ref);
-
- if (callbacks && callbacks->update_tips != NULL) {
- if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0)
- goto on_error;
- }
- }
-
- if (update_fetchhead &&
- (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
- goto on_error;
-
- git_vector_free(&update_heads);
- git_refspec__free(&tagspec);
- git_buf_free(&refname);
- return 0;
-
-on_error:
- git_vector_free(&update_heads);
- git_refspec__free(&tagspec);
- git_buf_free(&refname);
- return -1;
-
-}
-
-/**
- * Iteration over the three vectors, with a pause whenever we find a match
- *
- * On each stop, we store the iteration stat in the inout i,j,k
- * parameters, and return the currently matching passive refspec as
- * well as the head which we matched.
- */
-static int next_head(const git_remote *remote, git_vector *refs,
- git_refspec **out_spec, git_remote_head **out_head,
- size_t *out_i, size_t *out_j, size_t *out_k)
-{
- const git_vector *active, *passive;
- git_remote_head *head;
- git_refspec *spec, *passive_spec;
- size_t i, j, k;
-
- active = &remote->active_refspecs;
- passive = &remote->passive_refspecs;
-
- i = *out_i;
- j = *out_j;
- k = *out_k;
-
- for (; i < refs->length; i++) {
- head = git_vector_get(refs, i);
-
- if (!git_reference_is_valid_name(head->name))
- continue;
-
- for (; j < active->length; j++) {
- spec = git_vector_get(active, j);
-
- if (!git_refspec_src_matches(spec, head->name))
- continue;
-
- for (; k < passive->length; k++) {
- passive_spec = git_vector_get(passive, k);
-
- if (!git_refspec_src_matches(passive_spec, head->name))
- continue;
-
- *out_spec = passive_spec;
- *out_head = head;
- *out_i = i;
- *out_j = j;
- *out_k = k + 1;
- return 0;
-
- }
- k = 0;
- }
- j = 0;
- }
-
- return GIT_ITEROVER;
-}
-
-static int opportunistic_updates(const git_remote *remote, const git_remote_callbacks *callbacks,
- git_vector *refs, const char *msg)
-{
- size_t i, j, k;
- git_refspec *spec;
- git_remote_head *head;
- git_reference *ref;
- git_buf refname = GIT_BUF_INIT;
- int error = 0;
-
- i = j = k = 0;
-
- while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) {
- git_oid old = {{ 0 }};
- /*
- * If we got here, there is a refspec which was used
- * for fetching which matches the source of one of the
- * passive refspecs, so we should update that
- * remote-tracking branch, but not add it to
- * FETCH_HEAD
- */
-
- git_buf_clear(&refname);
- if ((error = git_refspec_transform(&refname, spec, head->name)) < 0)
- goto cleanup;
-
- error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- if (!git_oid_cmp(&old, &head->oid))
- continue;
-
- /* If we did find a current reference, make sure we haven't lost a race */
- if (error)
- error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, true, msg);
- else
- error = git_reference_create_matching(&ref, remote->repo, refname.ptr, &head->oid, true, &old, msg);
- git_reference_free(ref);
- if (error < 0)
- goto cleanup;
-
- if (callbacks && callbacks->update_tips != NULL) {
- if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0)
- goto cleanup;
- }
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
-cleanup:
- git_buf_free(&refname);
- return error;
-}
-
-int git_remote_update_tips(
- git_remote *remote,
- const git_remote_callbacks *callbacks,
- int update_fetchhead,
- git_remote_autotag_option_t download_tags,
- const char *reflog_message)
-{
- git_refspec *spec, tagspec;
- git_vector refs = GIT_VECTOR_INIT;
- git_remote_autotag_option_t tagopt;
- int error;
- size_t i;
-
- /* push has its own logic hidden away in the push object */
- if (remote->push) {
- return git_push_update_tips(remote->push, callbacks);
- }
-
- if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
- return -1;
-
-
- if ((error = ls_to_vector(&refs, remote)) < 0)
- goto out;
-
- if (download_tags == GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED)
- tagopt = remote->download_tags;
- else
- tagopt = download_tags;
-
- if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
- if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, &tagspec, &refs, reflog_message)) < 0)
- goto out;
- }
-
- git_vector_foreach(&remote->active_refspecs, i, spec) {
- if (spec->push)
- continue;
-
- if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, spec, &refs, reflog_message)) < 0)
- goto out;
- }
-
- /* only try to do opportunisitic updates if the refpec lists differ */
- if (remote->passed_refspecs)
- error = opportunistic_updates(remote, callbacks, &refs, reflog_message);
-
-out:
- git_vector_free(&refs);
- git_refspec__free(&tagspec);
- return error;
-}
-
-int git_remote_connected(const git_remote *remote)
-{
- assert(remote);
-
- if (!remote->transport || !remote->transport->is_connected)
- return 0;
-
- /* Ask the transport if it's connected. */
- return remote->transport->is_connected(remote->transport);
-}
-
-void git_remote_stop(git_remote *remote)
-{
- assert(remote);
-
- if (remote->transport && remote->transport->cancel)
- remote->transport->cancel(remote->transport);
-}
-
-void git_remote_disconnect(git_remote *remote)
-{
- assert(remote);
-
- if (git_remote_connected(remote))
- remote->transport->close(remote->transport);
-}
-
-void git_remote_free(git_remote *remote)
-{
- if (remote == NULL)
- return;
-
- if (remote->transport != NULL) {
- git_remote_disconnect(remote);
-
- remote->transport->free(remote->transport);
- remote->transport = NULL;
- }
-
- git_vector_free(&remote->refs);
-
- free_refspecs(&remote->refspecs);
- git_vector_free(&remote->refspecs);
-
- free_refspecs(&remote->active_refspecs);
- git_vector_free(&remote->active_refspecs);
-
- free_refspecs(&remote->passive_refspecs);
- git_vector_free(&remote->passive_refspecs);
-
- git_push_free(remote->push);
- git__free(remote->url);
- git__free(remote->pushurl);
- git__free(remote->name);
- git__free(remote);
-}
-
-static int remote_list_cb(const git_config_entry *entry, void *payload)
-{
- git_vector *list = payload;
- const char *name = entry->name + strlen("remote.");
- size_t namelen = strlen(name);
- char *remote_name;
-
- /* we know name matches "remote.<stuff>.(push)?url" */
-
- if (!strcmp(&name[namelen - 4], ".url"))
- remote_name = git__strndup(name, namelen - 4); /* strip ".url" */
- else
- remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */
- GITERR_CHECK_ALLOC(remote_name);
-
- return git_vector_insert(list, remote_name);
-}
-
-int git_remote_list(git_strarray *remotes_list, git_repository *repo)
-{
- int error;
- git_config *cfg;
- git_vector list = GIT_VECTOR_INIT;
-
- if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
- return error;
-
- if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0)
- return error;
-
- error = git_config_foreach_match(
- cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
-
- if (error < 0) {
- git_vector_free_deep(&list);
- return error;
- }
-
- git_vector_uniq(&list, git__free);
-
- remotes_list->strings =
- (char **)git_vector_detach(&remotes_list->count, NULL, &list);
-
- return 0;
-}
-
-const git_transfer_progress* git_remote_stats(git_remote *remote)
-{
- assert(remote);
- return &remote->stats;
-}
-
-git_remote_autotag_option_t git_remote_autotag(const git_remote *remote)
-{
- return remote->download_tags;
-}
-
-int git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value)
-{
- git_buf var = GIT_BUF_INIT;
- git_config *config;
- int error;
-
- assert(repo && remote);
-
- if ((error = ensure_remote_name_is_valid(remote)) < 0)
- return error;
-
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
- return error;
-
- if ((error = git_buf_printf(&var, CONFIG_TAGOPT_FMT, remote)))
- return error;
-
- switch (value) {
- case GIT_REMOTE_DOWNLOAD_TAGS_NONE:
- error = git_config_set_string(config, var.ptr, "--no-tags");
- break;
- case GIT_REMOTE_DOWNLOAD_TAGS_ALL:
- error = git_config_set_string(config, var.ptr, "--tags");
- break;
- case GIT_REMOTE_DOWNLOAD_TAGS_AUTO:
- error = git_config_delete_entry(config, var.ptr);
- if (error == GIT_ENOTFOUND)
- error = 0;
- break;
- default:
- giterr_set(GITERR_INVALID, "Invalid value for the tagopt setting");
- error = -1;
- }
-
- git_buf_free(&var);
- return error;
-}
-
-int git_remote_prune_refs(const git_remote *remote)
-{
- return remote->prune_refs;
-}
-
-static int rename_remote_config_section(
- git_repository *repo,
- const char *old_name,
- const char *new_name)
-{
- git_buf old_section_name = GIT_BUF_INIT,
- new_section_name = GIT_BUF_INIT;
- int error = -1;
-
- if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0)
- goto cleanup;
-
- if (new_name &&
- (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0))
- goto cleanup;
-
- error = git_config_rename_section(
- repo,
- git_buf_cstr(&old_section_name),
- new_name ? git_buf_cstr(&new_section_name) : NULL);
-
-cleanup:
- git_buf_free(&old_section_name);
- git_buf_free(&new_section_name);
-
- return error;
-}
-
-struct update_data {
- git_config *config;
- const char *old_remote_name;
- const char *new_remote_name;
-};
-
-static int update_config_entries_cb(
- const git_config_entry *entry,
- void *payload)
-{
- struct update_data *data = (struct update_data *)payload;
-
- if (strcmp(entry->value, data->old_remote_name))
- return 0;
-
- return git_config_set_string(
- data->config, entry->name, data->new_remote_name);
-}
-
-static int update_branch_remote_config_entry(
- git_repository *repo,
- const char *old_name,
- const char *new_name)
-{
- int error;
- struct update_data data = { NULL };
-
- if ((error = git_repository_config__weakptr(&data.config, repo)) < 0)
- return error;
-
- data.old_remote_name = old_name;
- data.new_remote_name = new_name;
-
- return git_config_foreach_match(
- data.config, "branch\\..+\\.remote", update_config_entries_cb, &data);
-}
-
-static int rename_one_remote_reference(
- git_reference *reference_in,
- const char *old_remote_name,
- const char *new_remote_name)
-{
- int error;
- git_reference *ref = NULL, *dummy = NULL;
- git_buf namespace = GIT_BUF_INIT, old_namespace = GIT_BUF_INIT;
- git_buf new_name = GIT_BUF_INIT;
- git_buf log_message = GIT_BUF_INIT;
- size_t pfx_len;
- const char *target;
-
- if ((error = git_buf_printf(&namespace, GIT_REFS_REMOTES_DIR "%s/", new_remote_name)) < 0)
- return error;
-
- pfx_len = strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name) + 1;
- git_buf_puts(&new_name, namespace.ptr);
- if ((error = git_buf_puts(&new_name, git_reference_name(reference_in) + pfx_len)) < 0)
- goto cleanup;
-
- if ((error = git_buf_printf(&log_message,
- "renamed remote %s to %s",
- old_remote_name, new_remote_name)) < 0)
- goto cleanup;
-
- if ((error = git_reference_rename(&ref, reference_in, git_buf_cstr(&new_name), 1,
- git_buf_cstr(&log_message))) < 0)
- goto cleanup;
-
- if (git_reference_type(ref) != GIT_REF_SYMBOLIC)
- goto cleanup;
-
- /* Handle refs like origin/HEAD -> origin/master */
- target = git_reference_symbolic_target(ref);
- if ((error = git_buf_printf(&old_namespace, GIT_REFS_REMOTES_DIR "%s/", old_remote_name)) < 0)
- goto cleanup;
-
- if (git__prefixcmp(target, old_namespace.ptr))
- goto cleanup;
-
- git_buf_clear(&new_name);
- git_buf_puts(&new_name, namespace.ptr);
- if ((error = git_buf_puts(&new_name, target + pfx_len)) < 0)
- goto cleanup;
-
- error = git_reference_symbolic_set_target(&dummy, ref, git_buf_cstr(&new_name),
- git_buf_cstr(&log_message));
-
- git_reference_free(dummy);
-
-cleanup:
- git_reference_free(reference_in);
- git_reference_free(ref);
- git_buf_free(&namespace);
- git_buf_free(&old_namespace);
- git_buf_free(&new_name);
- git_buf_free(&log_message);
- return error;
-}
-
-static int rename_remote_references(
- git_repository *repo,
- const char *old_name,
- const char *new_name)
-{
- int error;
- git_buf buf = GIT_BUF_INIT;
- git_reference *ref;
- git_reference_iterator *iter;
-
- if ((error = git_buf_printf(&buf, GIT_REFS_REMOTES_DIR "%s/*", old_name)) < 0)
- return error;
-
- error = git_reference_iterator_glob_new(&iter, repo, git_buf_cstr(&buf));
- git_buf_free(&buf);
-
- if (error < 0)
- return error;
-
- while ((error = git_reference_next(&ref, iter)) == 0) {
- if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0)
- break;
- }
-
- git_reference_iterator_free(iter);
-
- return (error == GIT_ITEROVER) ? 0 : error;
-}
-
-static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const char *new_name)
-{
- git_config *config;
- git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT;
- const git_refspec *spec;
- size_t i;
- int error = 0;
-
- if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0)
- return error;
-
- if ((error = git_vector_init(problems, 1, NULL)) < 0)
- return error;
-
- if ((error = git_buf_printf(
- &base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0)
- return error;
-
- git_vector_foreach(&remote->refspecs, i, spec) {
- if (spec->push)
- continue;
-
- /* Does the dst part of the refspec follow the expected format? */
- if (strcmp(git_buf_cstr(&base), spec->string)) {
- char *dup;
-
- dup = git__strdup(spec->string);
- GITERR_CHECK_ALLOC(dup);
-
- if ((error = git_vector_insert(problems, dup)) < 0)
- break;
-
- continue;
- }
-
- /* If we do want to move it to the new section */
-
- git_buf_clear(&val);
- git_buf_clear(&var);
-
- if (git_buf_printf(
- &val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0 ||
- git_buf_printf(&var, "remote.%s.fetch", new_name) < 0)
- {
- error = -1;
- break;
- }
-
- if ((error = git_config_set_string(
- config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0)
- break;
- }
-
- git_buf_free(&base);
- git_buf_free(&var);
- git_buf_free(&val);
-
- if (error < 0) {
- char *str;
- git_vector_foreach(problems, i, str)
- git__free(str);
-
- git_vector_free(problems);
- }
-
- return error;
-}
-
-int git_remote_rename(git_strarray *out, git_repository *repo, const char *name, const char *new_name)
-{
- int error;
- git_vector problem_refspecs = GIT_VECTOR_INIT;
- git_remote *remote = NULL;
-
- assert(out && repo && name && new_name);
-
- if ((error = git_remote_lookup(&remote, repo, name)) < 0)
- return error;
-
- if ((error = ensure_remote_name_is_valid(new_name)) < 0)
- goto cleanup;
-
- if ((error = ensure_remote_doesnot_exist(repo, new_name)) < 0)
- goto cleanup;
-
- if ((error = rename_remote_config_section(repo, name, new_name)) < 0)
- goto cleanup;
-
- if ((error = update_branch_remote_config_entry(repo, name, new_name)) < 0)
- goto cleanup;
-
- if ((error = rename_remote_references(repo, name, new_name)) < 0)
- goto cleanup;
-
- if ((error = rename_fetch_refspecs(&problem_refspecs, remote, new_name)) < 0)
- goto cleanup;
-
- out->count = problem_refspecs.length;
- out->strings = (char **) problem_refspecs.contents;
-
-cleanup:
- if (error < 0)
- git_vector_free(&problem_refspecs);
-
- git_remote_free(remote);
- return error;
-}
-
-int git_remote_is_valid_name(
- const char *remote_name)
-{
- git_buf buf = GIT_BUF_INIT;
- git_refspec refspec;
- int error = -1;
-
- if (!remote_name || *remote_name == '\0')
- return 0;
-
- git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name);
- error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true);
-
- git_buf_free(&buf);
- git_refspec__free(&refspec);
-
- giterr_clear();
- return error == 0;
-}
-
-git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname)
-{
- git_refspec *spec;
- size_t i;
-
- git_vector_foreach(&remote->active_refspecs, i, spec) {
- if (spec->push)
- continue;
-
- if (git_refspec_src_matches(spec, refname))
- return spec;
- }
-
- return NULL;
-}
-
-git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname)
-{
- git_refspec *spec;
- size_t i;
-
- git_vector_foreach(&remote->active_refspecs, i, spec) {
- if (spec->push)
- continue;
-
- if (git_refspec_dst_matches(spec, refname))
- return spec;
- }
-
- return NULL;
-}
-
-int git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec)
-{
- return write_add_refspec(repo, remote, refspec, true);
-}
-
-int git_remote_add_push(git_repository *repo, const char *remote, const char *refspec)
-{
- return write_add_refspec(repo, remote, refspec, false);
-}
-
-static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push)
-{
- size_t i;
- git_vector refspecs;
- git_refspec *spec;
- char *dup;
-
- if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0)
- return -1;
-
- git_vector_foreach(&remote->refspecs, i, spec) {
- if (spec->push != push)
- continue;
-
- if ((dup = git__strdup(spec->string)) == NULL)
- goto on_error;
-
- if (git_vector_insert(&refspecs, dup) < 0) {
- git__free(dup);
- goto on_error;
- }
- }
-
- array->strings = (char **)refspecs.contents;
- array->count = refspecs.length;
-
- return 0;
-
-on_error:
- git_vector_free_deep(&refspecs);
-
- return -1;
-}
-
-int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote)
-{
- return copy_refspecs(array, remote, false);
-}
-
-int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote)
-{
- return copy_refspecs(array, remote, true);
-}
-
-size_t git_remote_refspec_count(const git_remote *remote)
-{
- return remote->refspecs.length;
-}
-
-const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n)
-{
- return git_vector_get(&remote->refspecs, n);
-}
-
-int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT);
- return 0;
-}
-
-/* asserts a branch.<foo>.remote format */
-static const char *name_offset(size_t *len_out, const char *name)
-{
- size_t prefix_len;
- const char *dot;
-
- prefix_len = strlen("remote.");
- dot = strchr(name + prefix_len, '.');
-
- assert(dot);
-
- *len_out = dot - name - prefix_len;
- return name + prefix_len;
-}
-
-static int remove_branch_config_related_entries(
- git_repository *repo,
- const char *remote_name)
-{
- int error;
- git_config *config;
- git_config_entry *entry;
- git_config_iterator *iter;
- git_buf buf = GIT_BUF_INIT;
-
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
- return error;
-
- if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0)
- return error;
-
- /* find any branches with us as upstream and remove that config */
- while ((error = git_config_next(&entry, iter)) == 0) {
- const char *branch;
- size_t branch_len;
-
- if (strcmp(remote_name, entry->value))
- continue;
-
- branch = name_offset(&branch_len, entry->name);
-
- git_buf_clear(&buf);
- if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0)
- break;
-
- if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) {
- if (error != GIT_ENOTFOUND)
- break;
- giterr_clear();
- }
-
- git_buf_clear(&buf);
- if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0)
- break;
-
- if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) {
- if (error != GIT_ENOTFOUND)
- break;
- giterr_clear();
- }
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- git_buf_free(&buf);
- git_config_iterator_free(iter);
- return error;
-}
-
-static int remove_refs(git_repository *repo, const git_refspec *spec)
-{
- git_reference_iterator *iter = NULL;
- git_vector refs;
- const char *name;
- char *dup;
- int error;
- size_t i;
-
- if ((error = git_vector_init(&refs, 8, NULL)) < 0)
- return error;
-
- if ((error = git_reference_iterator_new(&iter, repo)) < 0)
- goto cleanup;
-
- while ((error = git_reference_next_name(&name, iter)) == 0) {
- if (!git_refspec_dst_matches(spec, name))
- continue;
-
- dup = git__strdup(name);
- if (!dup) {
- error = -1;
- goto cleanup;
- }
-
- if ((error = git_vector_insert(&refs, dup)) < 0)
- goto cleanup;
- }
- if (error == GIT_ITEROVER)
- error = 0;
- if (error < 0)
- goto cleanup;
-
- git_vector_foreach(&refs, i, name) {
- if ((error = git_reference_remove(repo, name)) < 0)
- break;
- }
-
-cleanup:
- git_reference_iterator_free(iter);
- git_vector_foreach(&refs, i, dup) {
- git__free(dup);
- }
- git_vector_free(&refs);
- return error;
-}
-
-static int remove_remote_tracking(git_repository *repo, const char *remote_name)
-{
- git_remote *remote;
- int error;
- size_t i, count;
-
- /* we want to use what's on the config, regardless of changes to the instance in memory */
- if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0)
- return error;
-
- count = git_remote_refspec_count(remote);
- for (i = 0; i < count; i++) {
- const git_refspec *refspec = git_remote_get_refspec(remote, i);
-
- /* shouldn't ever actually happen */
- if (refspec == NULL)
- continue;
-
- if ((error = remove_refs(repo, refspec)) < 0)
- break;
- }
-
- git_remote_free(remote);
- return error;
-}
-
-int git_remote_delete(git_repository *repo, const char *name)
-{
- int error;
-
- assert(repo && name);
-
- if ((error = remove_branch_config_related_entries(repo, name)) < 0 ||
- (error = remove_remote_tracking(repo, name)) < 0 ||
- (error = rename_remote_config_section(repo, name, NULL)) < 0)
- return error;
-
- return 0;
-}
-
-int git_remote_default_branch(git_buf *out, git_remote *remote)
-{
- const git_remote_head **heads;
- const git_remote_head *guess = NULL;
- const git_oid *head_id;
- size_t heads_len, i;
- int error;
-
- assert(out);
-
- if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
- return error;
-
- if (heads_len == 0)
- return GIT_ENOTFOUND;
-
- if (strcmp(heads[0]->name, GIT_HEAD_FILE))
- return GIT_ENOTFOUND;
-
- git_buf_sanitize(out);
- /* the first one must be HEAD so if that has the symref info, we're done */
- if (heads[0]->symref_target)
- return git_buf_puts(out, heads[0]->symref_target);
-
- /*
- * If there's no symref information, we have to look over them
- * and guess. We return the first match unless the master
- * branch is a candidate. Then we return the master branch.
- */
- head_id = &heads[0]->oid;
-
- for (i = 1; i < heads_len; i++) {
- if (git_oid_cmp(head_id, &heads[i]->oid))
- continue;
-
- if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR))
- continue;
-
- if (!guess) {
- guess = heads[i];
- continue;
- }
-
- if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) {
- guess = heads[i];
- break;
- }
- }
-
- if (!guess)
- return GIT_ENOTFOUND;
-
- return git_buf_puts(out, guess->name);
-}
-
-int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
-{
- size_t i;
- int error;
- git_push *push;
- git_refspec *spec;
- const git_remote_callbacks *cbs = NULL;
- const git_strarray *custom_headers = NULL;
- const git_proxy_options *proxy = NULL;
-
- assert(remote);
-
- if (opts) {
- cbs = &opts->callbacks;
- custom_headers = &opts->custom_headers;
- proxy = &opts->proxy_opts;
- }
-
- if (!git_remote_connected(remote) &&
- (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
- goto cleanup;
-
- free_refspecs(&remote->active_refspecs);
- if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
- goto cleanup;
-
- if (remote->push) {
- git_push_free(remote->push);
- remote->push = NULL;
- }
-
- if ((error = git_push_new(&remote->push, remote)) < 0)
- return error;
-
- push = remote->push;
-
- if (opts && (error = git_push_set_options(push, opts)) < 0)
- goto cleanup;
-
- if (refspecs && refspecs->count > 0) {
- for (i = 0; i < refspecs->count; i++) {
- if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0)
- goto cleanup;
- }
- } else {
- git_vector_foreach(&remote->refspecs, i, spec) {
- if (!spec->push)
- continue;
- if ((error = git_push_add_refspec(push, spec->string)) < 0)
- goto cleanup;
- }
- }
-
- if ((error = git_push_finish(push, cbs)) < 0)
- goto cleanup;
-
- if (cbs && cbs->push_update_reference &&
- (error = git_push_status_foreach(push, cbs->push_update_reference, cbs->payload)) < 0)
- goto cleanup;
-
-cleanup:
- return error;
-}
-
-int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
-{
- int error;
- const git_remote_callbacks *cbs = NULL;
- const git_strarray *custom_headers = NULL;
- const git_proxy_options *proxy = NULL;
-
- if (opts) {
- GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
- cbs = &opts->callbacks;
- custom_headers = &opts->custom_headers;
- GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
- proxy = &opts->proxy_opts;
- }
-
- assert(remote && refspecs);
-
- if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
- return error;
-
- if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
- return error;
-
- error = git_remote_update_tips(remote, cbs, 0, 0, NULL);
-
- git_remote_disconnect(remote);
- return error;
-}
-
-#define PREFIX "url"
-#define SUFFIX_FETCH "insteadof"
-#define SUFFIX_PUSH "pushinsteadof"
-
-char *apply_insteadof(git_config *config, const char *url, int direction)
-{
- size_t match_length, prefix_length, suffix_length;
- char *replacement = NULL;
- const char *regexp;
-
- git_buf result = GIT_BUF_INIT;
- git_config_entry *entry;
- git_config_iterator *iter;
-
- assert(config);
- assert(url);
- assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
-
- /* Add 1 to prefix/suffix length due to the additional escaped dot */
- prefix_length = strlen(PREFIX) + 1;
- if (direction == GIT_DIRECTION_FETCH) {
- regexp = PREFIX "\\..*\\." SUFFIX_FETCH;
- suffix_length = strlen(SUFFIX_FETCH) + 1;
- } else {
- regexp = PREFIX "\\..*\\." SUFFIX_PUSH;
- suffix_length = strlen(SUFFIX_PUSH) + 1;
- }
-
- if (git_config_iterator_glob_new(&iter, config, regexp) < 0)
- return NULL;
-
- match_length = 0;
- while (git_config_next(&entry, iter) == 0) {
- size_t n, replacement_length;
-
- /* Check if entry value is a prefix of URL */
- if (git__prefixcmp(url, entry->value))
- continue;
- /* Check if entry value is longer than previous
- * prefixes */
- if ((n = strlen(entry->value)) <= match_length)
- continue;
-
- git__free(replacement);
- match_length = n;
-
- /* Cut off prefix and suffix of the value */
- replacement_length =
- strlen(entry->name) - (prefix_length + suffix_length);
- replacement = git__strndup(entry->name + prefix_length,
- replacement_length);
- }
-
- git_config_iterator_free(iter);
-
- if (match_length == 0)
- return git__strdup(url);
-
- git_buf_printf(&result, "%s%s", replacement, url + match_length);
-
- git__free(replacement);
-
- return result.ptr;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_remote_h__
-#define INCLUDE_remote_h__
-
-#include "git2/remote.h"
-#include "git2/transport.h"
-#include "git2/sys/transport.h"
-
-#include "refspec.h"
-#include "vector.h"
-
-#define GIT_REMOTE_ORIGIN "origin"
-
-struct git_remote {
- char *name;
- char *url;
- char *pushurl;
- git_vector refs;
- git_vector refspecs;
- git_vector active_refspecs;
- git_vector passive_refspecs;
- git_transport *transport;
- git_repository *repo;
- git_push *push;
- git_transfer_progress stats;
- unsigned int need_pack;
- git_remote_autotag_option_t download_tags;
- int prune_refs;
- int passed_refspecs;
-};
-
-const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
-int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url);
-
-git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname);
-git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_repo_template_h__
-#define INCLUDE_repo_template_h__
-
-#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
-#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
-
-#define GIT_HOOKS_DIR "hooks/"
-#define GIT_HOOKS_DIR_MODE 0777
-
-#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
-#define GIT_HOOKS_README_MODE 0777
-#define GIT_HOOKS_README_CONTENT \
-"#!/bin/sh\n"\
-"#\n"\
-"# Place appropriately named executable hook scripts into this directory\n"\
-"# to intercept various actions that git takes. See `git help hooks` for\n"\
-"# more information.\n"
-
-#define GIT_INFO_DIR "info/"
-#define GIT_INFO_DIR_MODE 0777
-
-#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
-#define GIT_INFO_EXCLUDE_MODE 0666
-#define GIT_INFO_EXCLUDE_CONTENT \
-"# File patterns to ignore; see `git help ignore` for more information.\n"\
-"# Lines that start with '#' are comments.\n"
-
-#define GIT_DESC_FILE "description"
-#define GIT_DESC_MODE 0666
-#define GIT_DESC_CONTENT \
-"Unnamed repository; edit this file 'description' to name the repository.\n"
-
-typedef struct {
- const char *path;
- mode_t mode;
- const char *content;
-} repo_template_item;
-
-static repo_template_item repo_template[] = {
- { GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/info/' */
- { GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/pack/' */
- { GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/heads/' */
- { GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/tags/' */
- { GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE, NULL }, /* '/hooks/' */
- { GIT_INFO_DIR, GIT_INFO_DIR_MODE, NULL }, /* '/info/' */
- { GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
- { GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
- { GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
- { NULL, 0, NULL }
-};
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include <ctype.h>
-
-#include "git2/object.h"
-#include "git2/refdb.h"
-#include "git2/sys/repository.h"
-
-#include "common.h"
-#include "repository.h"
-#include "commit.h"
-#include "tag.h"
-#include "blob.h"
-#include "fileops.h"
-#include "sysdir.h"
-#include "filebuf.h"
-#include "index.h"
-#include "config.h"
-#include "refs.h"
-#include "filter.h"
-#include "odb.h"
-#include "remote.h"
-#include "merge.h"
-#include "diff_driver.h"
-#include "annotated_commit.h"
-
-#ifdef GIT_WIN32
-# include "win32/w32_util.h"
-#endif
-
-static int check_repositoryformatversion(git_config *config);
-
-#define GIT_FILE_CONTENT_PREFIX "gitdir:"
-
-#define GIT_BRANCH_MASTER "master"
-
-#define GIT_REPO_VERSION 0
-
-git_buf git_repository__reserved_names_win32[] = {
- { DOT_GIT, 0, CONST_STRLEN(DOT_GIT) },
- { GIT_DIR_SHORTNAME, 0, CONST_STRLEN(GIT_DIR_SHORTNAME) }
-};
-size_t git_repository__reserved_names_win32_len = 2;
-
-git_buf git_repository__reserved_names_posix[] = {
- { DOT_GIT, 0, CONST_STRLEN(DOT_GIT) },
-};
-size_t git_repository__reserved_names_posix_len = 1;
-
-static void set_odb(git_repository *repo, git_odb *odb)
-{
- if (odb) {
- GIT_REFCOUNT_OWN(odb, repo);
- GIT_REFCOUNT_INC(odb);
- }
-
- if ((odb = git__swap(repo->_odb, odb)) != NULL) {
- GIT_REFCOUNT_OWN(odb, NULL);
- git_odb_free(odb);
- }
-}
-
-static void set_refdb(git_repository *repo, git_refdb *refdb)
-{
- if (refdb) {
- GIT_REFCOUNT_OWN(refdb, repo);
- GIT_REFCOUNT_INC(refdb);
- }
-
- if ((refdb = git__swap(repo->_refdb, refdb)) != NULL) {
- GIT_REFCOUNT_OWN(refdb, NULL);
- git_refdb_free(refdb);
- }
-}
-
-static void set_config(git_repository *repo, git_config *config)
-{
- if (config) {
- GIT_REFCOUNT_OWN(config, repo);
- GIT_REFCOUNT_INC(config);
- }
-
- if ((config = git__swap(repo->_config, config)) != NULL) {
- GIT_REFCOUNT_OWN(config, NULL);
- git_config_free(config);
- }
-
- git_repository__cvar_cache_clear(repo);
-}
-
-static void set_index(git_repository *repo, git_index *index)
-{
- if (index) {
- GIT_REFCOUNT_OWN(index, repo);
- GIT_REFCOUNT_INC(index);
- }
-
- if ((index = git__swap(repo->_index, index)) != NULL) {
- GIT_REFCOUNT_OWN(index, NULL);
- git_index_free(index);
- }
-}
-
-void git_repository__cleanup(git_repository *repo)
-{
- assert(repo);
-
- git_cache_clear(&repo->objects);
- git_attr_cache_flush(repo);
-
- set_config(repo, NULL);
- set_index(repo, NULL);
- set_odb(repo, NULL);
- set_refdb(repo, NULL);
-}
-
-void git_repository_free(git_repository *repo)
-{
- size_t i;
-
- if (repo == NULL)
- return;
-
- git_repository__cleanup(repo);
-
- git_cache_free(&repo->objects);
-
- git_diff_driver_registry_free(repo->diff_drivers);
- repo->diff_drivers = NULL;
-
- for (i = 0; i < repo->reserved_names.size; i++)
- git_buf_free(git_array_get(repo->reserved_names, i));
- git_array_clear(repo->reserved_names);
-
- git__free(repo->path_gitlink);
- git__free(repo->path_repository);
- git__free(repo->workdir);
- git__free(repo->namespace);
- git__free(repo->ident_name);
- git__free(repo->ident_email);
-
- git__memzero(repo, sizeof(*repo));
- git__free(repo);
-}
-
-/*
- * Git repository open methods
- *
- * Open a repository object from its path
- */
-static bool valid_repository_path(git_buf *repository_path)
-{
- /* Check OBJECTS_DIR first, since it will generate the longest path name */
- if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)
- return false;
-
- /* Ensure HEAD file exists */
- if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
- return false;
-
- if (git_path_contains_dir(repository_path, GIT_REFS_DIR) == false)
- return false;
-
- return true;
-}
-
-static git_repository *repository_alloc(void)
-{
- git_repository *repo = git__calloc(1, sizeof(git_repository));
-
- if (repo == NULL ||
- git_cache_init(&repo->objects) < 0)
- goto on_error;
-
- git_array_init_to_size(repo->reserved_names, 4);
- if (!repo->reserved_names.ptr)
- goto on_error;
-
- /* set all the entries in the cvar cache to `unset` */
- git_repository__cvar_cache_clear(repo);
-
- return repo;
-
-on_error:
- if (repo)
- git_cache_free(&repo->objects);
-
- git__free(repo);
- return NULL;
-}
-
-int git_repository_new(git_repository **out)
-{
- git_repository *repo;
-
- *out = repo = repository_alloc();
- GITERR_CHECK_ALLOC(repo);
-
- repo->is_bare = 1;
-
- return 0;
-}
-
-static int load_config_data(git_repository *repo, const git_config *config)
-{
- int is_bare;
-
- /* Try to figure out if it's bare, default to non-bare if it's not set */
- if (git_config_get_bool(&is_bare, config, "core.bare") < 0)
- repo->is_bare = 0;
- else
- repo->is_bare = is_bare;
-
- return 0;
-}
-
-static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path)
-{
- int error;
- git_config_entry *ce;
- git_buf worktree = GIT_BUF_INIT;
-
- if (repo->is_bare)
- return 0;
-
- if ((error = git_config__lookup_entry(
- &ce, config, "core.worktree", false)) < 0)
- return error;
-
- if (ce && ce->value) {
- if ((error = git_path_prettify_dir(
- &worktree, ce->value, repo->path_repository)) < 0)
- goto cleanup;
-
- repo->workdir = git_buf_detach(&worktree);
- }
- else if (parent_path && git_path_isdir(parent_path->ptr))
- repo->workdir = git_buf_detach(parent_path);
- else {
- if (git_path_dirname_r(&worktree, repo->path_repository) < 0 ||
- git_path_to_dir(&worktree) < 0) {
- error = -1;
- goto cleanup;
- }
-
- repo->workdir = git_buf_detach(&worktree);
- }
-
- GITERR_CHECK_ALLOC(repo->workdir);
-cleanup:
- git_config_entry_free(ce);
- return error;
-}
-
-/*
- * This function returns furthest offset into path where a ceiling dir
- * is found, so we can stop processing the path at that point.
- *
- * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on
- * the stack could remove directories name limits, but at the cost of doing
- * repeated malloc/frees inside the loop below, so let's not do it now.
- */
-static size_t find_ceiling_dir_offset(
- const char *path,
- const char *ceiling_directories)
-{
- char buf[GIT_PATH_MAX + 1];
- char buf2[GIT_PATH_MAX + 1];
- const char *ceil, *sep;
- size_t len, max_len = 0, min_len;
-
- assert(path);
-
- min_len = (size_t)(git_path_root(path) + 1);
-
- if (ceiling_directories == NULL || min_len == 0)
- return min_len;
-
- for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
- for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
- len = sep - ceil;
-
- if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1)
- continue;
-
- strncpy(buf, ceil, len);
- buf[len] = '\0';
-
- if (p_realpath(buf, buf2) == NULL)
- continue;
-
- len = strlen(buf2);
- if (len > 0 && buf2[len-1] == '/')
- buf[--len] = '\0';
-
- if (!strncmp(path, buf2, len) &&
- (path[len] == '/' || !path[len]) &&
- len > max_len)
- {
- max_len = len;
- }
- }
-
- return (max_len <= min_len ? min_len : max_len);
-}
-
-/*
- * Read the contents of `file_path` and set `path_out` to the repo dir that
- * it points to. Before calling, set `path_out` to the base directory that
- * should be used if the contents of `file_path` are a relative path.
- */
-static int read_gitfile(git_buf *path_out, const char *file_path)
-{
- int error = 0;
- git_buf file = GIT_BUF_INIT;
- size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX);
-
- assert(path_out && file_path);
-
- if (git_futils_readbuffer(&file, file_path) < 0)
- return -1;
-
- git_buf_rtrim(&file);
- /* apparently on Windows, some people use backslashes in paths */
- git_path_mkposix(file.ptr);
-
- if (git_buf_len(&file) <= prefix_len ||
- memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
- {
- giterr_set(GITERR_REPOSITORY,
- "The `.git` file at '%s' is malformed", file_path);
- error = -1;
- }
- else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
- const char *gitlink = git_buf_cstr(&file) + prefix_len;
- while (*gitlink && git__isspace(*gitlink)) gitlink++;
-
- error = git_path_prettify_dir(
- path_out, gitlink, git_buf_cstr(path_out));
- }
-
- git_buf_free(&file);
- return error;
-}
-
-static int find_repo(
- git_buf *repo_path,
- git_buf *parent_path,
- git_buf *link_path,
- const char *start_path,
- uint32_t flags,
- const char *ceiling_dirs)
-{
- int error;
- git_buf path = GIT_BUF_INIT;
- git_buf repo_link = GIT_BUF_INIT;
- struct stat st;
- dev_t initial_device = 0;
- int min_iterations;
- bool in_dot_git;
- size_t ceiling_offset = 0;
-
- git_buf_free(repo_path);
-
- error = git_path_prettify(&path, start_path, NULL);
- if (error < 0)
- return error;
-
- /* in_dot_git toggles each loop:
- * /a/b/c/.git, /a/b/c, /a/b/.git, /a/b, /a/.git, /a
- * With GIT_REPOSITORY_OPEN_BARE or GIT_REPOSITORY_OPEN_NO_DOTGIT, we
- * assume we started with /a/b/c.git and don't append .git the first
- * time through.
- * min_iterations indicates the number of iterations left before going
- * further counts as a search. */
- if (flags & (GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
- in_dot_git = true;
- min_iterations = 1;
- } else {
- in_dot_git = false;
- min_iterations = 2;
- }
-
- for (;;) {
- if (!(flags & GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
- if (!in_dot_git) {
- error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
- if (error < 0)
- break;
- }
- in_dot_git = !in_dot_git;
- }
-
- if (p_stat(path.ptr, &st) == 0) {
- /* check that we have not crossed device boundaries */
- if (initial_device == 0)
- initial_device = st.st_dev;
- else if (st.st_dev != initial_device &&
- !(flags & GIT_REPOSITORY_OPEN_CROSS_FS))
- break;
-
- if (S_ISDIR(st.st_mode)) {
- if (valid_repository_path(&path)) {
- git_path_to_dir(&path);
- git_buf_set(repo_path, path.ptr, path.size);
- break;
- }
- }
- else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) {
- error = read_gitfile(&repo_link, path.ptr);
- if (error < 0)
- break;
- if (valid_repository_path(&repo_link)) {
- git_buf_swap(repo_path, &repo_link);
-
- if (link_path)
- error = git_buf_put(link_path, path.ptr, path.size);
- }
- break;
- }
- }
-
- /* Move up one directory. If we're in_dot_git, we'll search the
- * parent itself next. If we're !in_dot_git, we'll search .git
- * in the parent directory next (added at the top of the loop). */
- if (git_path_dirname_r(&path, path.ptr) < 0) {
- error = -1;
- break;
- }
-
- /* Once we've checked the directory (and .git if applicable),
- * find the ceiling for a search. */
- if (min_iterations && (--min_iterations == 0))
- ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
-
- /* Check if we should stop searching here. */
- if (min_iterations == 0
- && (path.ptr[ceiling_offset] == 0
- || (flags & GIT_REPOSITORY_OPEN_NO_SEARCH)))
- break;
- }
-
- if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
- if (!git_buf_len(repo_path))
- git_buf_clear(parent_path);
- else {
- git_path_dirname_r(parent_path, path.ptr);
- git_path_to_dir(parent_path);
- }
- if (git_buf_oom(parent_path))
- return -1;
- }
-
- /* If we didn't find the repository, and we don't have any other error
- * to report, report that. */
- if (!git_buf_len(repo_path) && !error) {
- giterr_set(GITERR_REPOSITORY,
- "Could not find repository from '%s'", start_path);
- error = GIT_ENOTFOUND;
- }
-
- git_buf_free(&path);
- git_buf_free(&repo_link);
- return error;
-}
-
-int git_repository_open_bare(
- git_repository **repo_ptr,
- const char *bare_path)
-{
- int error;
- git_buf path = GIT_BUF_INIT;
- git_repository *repo = NULL;
-
- if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0)
- return error;
-
- if (!valid_repository_path(&path)) {
- git_buf_free(&path);
- giterr_set(GITERR_REPOSITORY, "Path is not a repository: %s", bare_path);
- return GIT_ENOTFOUND;
- }
-
- repo = repository_alloc();
- GITERR_CHECK_ALLOC(repo);
-
- repo->path_repository = git_buf_detach(&path);
- GITERR_CHECK_ALLOC(repo->path_repository);
-
- /* of course we're bare! */
- repo->is_bare = 1;
- repo->workdir = NULL;
-
- *repo_ptr = repo;
- return 0;
-}
-
-static int _git_repository_open_ext_from_env(
- git_repository **out,
- const char *start_path)
-{
- git_repository *repo = NULL;
- git_index *index = NULL;
- git_odb *odb = NULL;
- git_buf dir_buf = GIT_BUF_INIT;
- git_buf ceiling_dirs_buf = GIT_BUF_INIT;
- git_buf across_fs_buf = GIT_BUF_INIT;
- git_buf index_file_buf = GIT_BUF_INIT;
- git_buf namespace_buf = GIT_BUF_INIT;
- git_buf object_dir_buf = GIT_BUF_INIT;
- git_buf alts_buf = GIT_BUF_INIT;
- git_buf work_tree_buf = GIT_BUF_INIT;
- git_buf common_dir_buf = GIT_BUF_INIT;
- const char *ceiling_dirs = NULL;
- unsigned flags = 0;
- int error;
-
- if (!start_path) {
- error = git__getenv(&dir_buf, "GIT_DIR");
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- start_path = ".";
- } else if (error < 0)
- goto error;
- else {
- start_path = git_buf_cstr(&dir_buf);
- flags |= GIT_REPOSITORY_OPEN_NO_SEARCH;
- flags |= GIT_REPOSITORY_OPEN_NO_DOTGIT;
- }
- }
-
- error = git__getenv(&ceiling_dirs_buf, "GIT_CEILING_DIRECTORIES");
- if (error == GIT_ENOTFOUND)
- giterr_clear();
- else if (error < 0)
- goto error;
- else
- ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
-
- error = git__getenv(&across_fs_buf, "GIT_DISCOVERY_ACROSS_FILESYSTEM");
- if (error == GIT_ENOTFOUND)
- giterr_clear();
- else if (error < 0)
- goto error;
- else {
- int across_fs = 0;
- error = git_config_parse_bool(&across_fs, git_buf_cstr(&across_fs_buf));
- if (error < 0)
- goto error;
- if (across_fs)
- flags |= GIT_REPOSITORY_OPEN_CROSS_FS;
- }
-
- error = git__getenv(&index_file_buf, "GIT_INDEX_FILE");
- if (error == GIT_ENOTFOUND)
- giterr_clear();
- else if (error < 0)
- goto error;
- else {
- error = git_index_open(&index, git_buf_cstr(&index_file_buf));
- if (error < 0)
- goto error;
- }
-
- error = git__getenv(&namespace_buf, "GIT_NAMESPACE");
- if (error == GIT_ENOTFOUND)
- giterr_clear();
- else if (error < 0)
- goto error;
-
- error = git__getenv(&object_dir_buf, "GIT_OBJECT_DIRECTORY");
- if (error == GIT_ENOTFOUND)
- giterr_clear();
- else if (error < 0)
- goto error;
- else {
- error = git_odb_open(&odb, git_buf_cstr(&object_dir_buf));
- if (error < 0)
- goto error;
- }
-
- error = git__getenv(&work_tree_buf, "GIT_WORK_TREE");
- if (error == GIT_ENOTFOUND)
- giterr_clear();
- else if (error < 0)
- goto error;
- else {
- giterr_set(GITERR_INVALID, "GIT_WORK_TREE unimplemented");
- error = GIT_ERROR;
- goto error;
- }
-
- error = git__getenv(&work_tree_buf, "GIT_COMMON_DIR");
- if (error == GIT_ENOTFOUND)
- giterr_clear();
- else if (error < 0)
- goto error;
- else {
- giterr_set(GITERR_INVALID, "GIT_COMMON_DIR unimplemented");
- error = GIT_ERROR;
- goto error;
- }
-
- error = git_repository_open_ext(&repo, start_path, flags, ceiling_dirs);
- if (error < 0)
- goto error;
-
- if (odb)
- git_repository_set_odb(repo, odb);
-
- error = git__getenv(&alts_buf, "GIT_ALTERNATE_OBJECT_DIRECTORIES");
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- } else if (error < 0)
- goto error;
- else {
- const char *end;
- char *alt, *sep;
- if (!odb) {
- error = git_repository_odb(&odb, repo);
- if (error < 0)
- goto error;
- }
-
- end = git_buf_cstr(&alts_buf) + git_buf_len(&alts_buf);
- for (sep = alt = alts_buf.ptr; sep != end; alt = sep+1) {
- for (sep = alt; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++)
- ;
- if (*sep)
- *sep = '\0';
- error = git_odb_add_disk_alternate(odb, alt);
- if (error < 0)
- goto error;
- }
- }
-
- if (git_buf_len(&namespace_buf)) {
- error = git_repository_set_namespace(repo, git_buf_cstr(&namespace_buf));
- if (error < 0)
- goto error;
- }
-
- git_repository_set_index(repo, index);
-
- if (out) {
- *out = repo;
- goto success;
- }
-error:
- git_repository_free(repo);
-success:
- git_odb_free(odb);
- git_index_free(index);
- git_buf_free(&common_dir_buf);
- git_buf_free(&work_tree_buf);
- git_buf_free(&alts_buf);
- git_buf_free(&object_dir_buf);
- git_buf_free(&namespace_buf);
- git_buf_free(&index_file_buf);
- git_buf_free(&across_fs_buf);
- git_buf_free(&ceiling_dirs_buf);
- git_buf_free(&dir_buf);
- return error;
-}
-
-int git_repository_open_ext(
- git_repository **repo_ptr,
- const char *start_path,
- unsigned int flags,
- const char *ceiling_dirs)
-{
- int error;
- git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT,
- link_path = GIT_BUF_INIT;
- git_repository *repo;
- git_config *config = NULL;
-
- if (flags & GIT_REPOSITORY_OPEN_FROM_ENV)
- return _git_repository_open_ext_from_env(repo_ptr, start_path);
-
- if (repo_ptr)
- *repo_ptr = NULL;
-
- error = find_repo(
- &path, &parent, &link_path, start_path, flags, ceiling_dirs);
-
- if (error < 0 || !repo_ptr)
- return error;
-
- repo = repository_alloc();
- GITERR_CHECK_ALLOC(repo);
-
- repo->path_repository = git_buf_detach(&path);
- GITERR_CHECK_ALLOC(repo->path_repository);
-
- if (link_path.size) {
- repo->path_gitlink = git_buf_detach(&link_path);
- GITERR_CHECK_ALLOC(repo->path_gitlink);
- }
-
- /*
- * We'd like to have the config, but git doesn't particularly
- * care if it's not there, so we need to deal with that.
- */
-
- error = git_repository_config_snapshot(&config, repo);
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- if (config && (error = check_repositoryformatversion(config)) < 0)
- goto cleanup;
-
- if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
- repo->is_bare = 1;
- else {
-
- if (config &&
- ((error = load_config_data(repo, config)) < 0 ||
- (error = load_workdir(repo, config, &parent)) < 0))
- goto cleanup;
- }
-
-cleanup:
- git_buf_free(&parent);
- git_config_free(config);
-
- if (error < 0)
- git_repository_free(repo);
- else
- *repo_ptr = repo;
-
- return error;
-}
-
-int git_repository_open(git_repository **repo_out, const char *path)
-{
- return git_repository_open_ext(
- repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);
-}
-
-int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb)
-{
- git_repository *repo;
-
- repo = repository_alloc();
- GITERR_CHECK_ALLOC(repo);
-
- git_repository_set_odb(repo, odb);
- *repo_out = repo;
-
- return 0;
-}
-
-int git_repository_discover(
- git_buf *out,
- const char *start_path,
- int across_fs,
- const char *ceiling_dirs)
-{
- uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
-
- assert(start_path);
-
- git_buf_sanitize(out);
-
- return find_repo(out, NULL, NULL, start_path, flags, ceiling_dirs);
-}
-
-static int load_config(
- git_config **out,
- git_repository *repo,
- const char *global_config_path,
- const char *xdg_config_path,
- const char *system_config_path,
- const char *programdata_path)
-{
- int error;
- git_buf config_path = GIT_BUF_INIT;
- git_config *cfg = NULL;
-
- assert(repo && out);
-
- if ((error = git_config_new(&cfg)) < 0)
- return error;
-
- error = git_buf_joinpath(
- &config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO);
- if (error < 0)
- goto on_error;
-
- if ((error = git_config_add_file_ondisk(
- cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0 &&
- error != GIT_ENOTFOUND)
- goto on_error;
-
- git_buf_free(&config_path);
-
- if (global_config_path != NULL &&
- (error = git_config_add_file_ondisk(
- cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, 0)) < 0 &&
- error != GIT_ENOTFOUND)
- goto on_error;
-
- if (xdg_config_path != NULL &&
- (error = git_config_add_file_ondisk(
- cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, 0)) < 0 &&
- error != GIT_ENOTFOUND)
- goto on_error;
-
- if (system_config_path != NULL &&
- (error = git_config_add_file_ondisk(
- cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, 0)) < 0 &&
- error != GIT_ENOTFOUND)
- goto on_error;
-
- if (programdata_path != NULL &&
- (error = git_config_add_file_ondisk(
- cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, 0)) < 0 &&
- error != GIT_ENOTFOUND)
- goto on_error;
-
- giterr_clear(); /* clear any lingering ENOTFOUND errors */
-
- *out = cfg;
- return 0;
-
-on_error:
- git_buf_free(&config_path);
- git_config_free(cfg);
- *out = NULL;
- return error;
-}
-
-static const char *path_unless_empty(git_buf *buf)
-{
- return git_buf_len(buf) > 0 ? git_buf_cstr(buf) : NULL;
-}
-
-int git_repository_config__weakptr(git_config **out, git_repository *repo)
-{
- int error = 0;
-
- if (repo->_config == NULL) {
- git_buf global_buf = GIT_BUF_INIT;
- git_buf xdg_buf = GIT_BUF_INIT;
- git_buf system_buf = GIT_BUF_INIT;
- git_buf programdata_buf = GIT_BUF_INIT;
- git_config *config;
-
- git_config_find_global(&global_buf);
- git_config_find_xdg(&xdg_buf);
- git_config_find_system(&system_buf);
- git_config_find_programdata(&programdata_buf);
-
- /* If there is no global file, open a backend for it anyway */
- if (git_buf_len(&global_buf) == 0)
- git_config__global_location(&global_buf);
-
- error = load_config(
- &config, repo,
- path_unless_empty(&global_buf),
- path_unless_empty(&xdg_buf),
- path_unless_empty(&system_buf),
- path_unless_empty(&programdata_buf));
- if (!error) {
- GIT_REFCOUNT_OWN(config, repo);
-
- config = git__compare_and_swap(&repo->_config, NULL, config);
- if (config != NULL) {
- GIT_REFCOUNT_OWN(config, NULL);
- git_config_free(config);
- }
- }
-
- git_buf_free(&global_buf);
- git_buf_free(&xdg_buf);
- git_buf_free(&system_buf);
- git_buf_free(&programdata_buf);
- }
-
- *out = repo->_config;
- return error;
-}
-
-int git_repository_config(git_config **out, git_repository *repo)
-{
- if (git_repository_config__weakptr(out, repo) < 0)
- return -1;
-
- GIT_REFCOUNT_INC(*out);
- return 0;
-}
-
-int git_repository_config_snapshot(git_config **out, git_repository *repo)
-{
- int error;
- git_config *weak;
-
- if ((error = git_repository_config__weakptr(&weak, repo)) < 0)
- return error;
-
- return git_config_snapshot(out, weak);
-}
-
-void git_repository_set_config(git_repository *repo, git_config *config)
-{
- assert(repo && config);
- set_config(repo, config);
-}
-
-int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
-{
- int error = 0;
-
- assert(repo && out);
-
- if (repo->_odb == NULL) {
- git_buf odb_path = GIT_BUF_INIT;
- git_odb *odb;
-
- if ((error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR)) < 0)
- return error;
-
- error = git_odb_open(&odb, odb_path.ptr);
- if (!error) {
- GIT_REFCOUNT_OWN(odb, repo);
-
- odb = git__compare_and_swap(&repo->_odb, NULL, odb);
- if (odb != NULL) {
- GIT_REFCOUNT_OWN(odb, NULL);
- git_odb_free(odb);
- }
- }
-
- git_buf_free(&odb_path);
- }
-
- *out = repo->_odb;
- return error;
-}
-
-int git_repository_odb(git_odb **out, git_repository *repo)
-{
- if (git_repository_odb__weakptr(out, repo) < 0)
- return -1;
-
- GIT_REFCOUNT_INC(*out);
- return 0;
-}
-
-void git_repository_set_odb(git_repository *repo, git_odb *odb)
-{
- assert(repo && odb);
- set_odb(repo, odb);
-}
-
-int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
-{
- int error = 0;
-
- assert(out && repo);
-
- if (repo->_refdb == NULL) {
- git_refdb *refdb;
-
- error = git_refdb_open(&refdb, repo);
- if (!error) {
- GIT_REFCOUNT_OWN(refdb, repo);
-
- refdb = git__compare_and_swap(&repo->_refdb, NULL, refdb);
- if (refdb != NULL) {
- GIT_REFCOUNT_OWN(refdb, NULL);
- git_refdb_free(refdb);
- }
- }
- }
-
- *out = repo->_refdb;
- return error;
-}
-
-int git_repository_refdb(git_refdb **out, git_repository *repo)
-{
- if (git_repository_refdb__weakptr(out, repo) < 0)
- return -1;
-
- GIT_REFCOUNT_INC(*out);
- return 0;
-}
-
-void git_repository_set_refdb(git_repository *repo, git_refdb *refdb)
-{
- assert(repo && refdb);
- set_refdb(repo, refdb);
-}
-
-int git_repository_index__weakptr(git_index **out, git_repository *repo)
-{
- int error = 0;
-
- assert(out && repo);
-
- if (repo->_index == NULL) {
- git_buf index_path = GIT_BUF_INIT;
- git_index *index;
-
- if ((error = git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE)) < 0)
- return error;
-
- error = git_index_open(&index, index_path.ptr);
- if (!error) {
- GIT_REFCOUNT_OWN(index, repo);
-
- index = git__compare_and_swap(&repo->_index, NULL, index);
- if (index != NULL) {
- GIT_REFCOUNT_OWN(index, NULL);
- git_index_free(index);
- }
-
- error = git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER);
- }
-
- git_buf_free(&index_path);
- }
-
- *out = repo->_index;
- return error;
-}
-
-int git_repository_index(git_index **out, git_repository *repo)
-{
- if (git_repository_index__weakptr(out, repo) < 0)
- return -1;
-
- GIT_REFCOUNT_INC(*out);
- return 0;
-}
-
-void git_repository_set_index(git_repository *repo, git_index *index)
-{
- assert(repo);
- set_index(repo, index);
-}
-
-int git_repository_set_namespace(git_repository *repo, const char *namespace)
-{
- git__free(repo->namespace);
-
- if (namespace == NULL) {
- repo->namespace = NULL;
- return 0;
- }
-
- return (repo->namespace = git__strdup(namespace)) ? 0 : -1;
-}
-
-const char *git_repository_get_namespace(git_repository *repo)
-{
- return repo->namespace;
-}
-
-#ifdef GIT_WIN32
-static int reserved_names_add8dot3(git_repository *repo, const char *path)
-{
- char *name = git_win32_path_8dot3_name(path);
- const char *def = GIT_DIR_SHORTNAME;
- const char *def_dot_git = DOT_GIT;
- size_t name_len, def_len = CONST_STRLEN(GIT_DIR_SHORTNAME);
- size_t def_dot_git_len = CONST_STRLEN(DOT_GIT);
- git_buf *buf;
-
- if (!name)
- return 0;
-
- name_len = strlen(name);
-
- if ((name_len == def_len && memcmp(name, def, def_len) == 0) ||
- (name_len == def_dot_git_len && memcmp(name, def_dot_git, def_dot_git_len) == 0)) {
- git__free(name);
- return 0;
- }
-
- if ((buf = git_array_alloc(repo->reserved_names)) == NULL)
- return -1;
-
- git_buf_attach(buf, name, name_len);
- return true;
-}
-
-bool git_repository__reserved_names(
- git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs)
-{
- GIT_UNUSED(include_ntfs);
-
- if (repo->reserved_names.size == 0) {
- git_buf *buf;
- size_t i;
-
- /* Add the static defaults */
- for (i = 0; i < git_repository__reserved_names_win32_len; i++) {
- if ((buf = git_array_alloc(repo->reserved_names)) == NULL)
- goto on_error;
-
- buf->ptr = git_repository__reserved_names_win32[i].ptr;
- buf->size = git_repository__reserved_names_win32[i].size;
- }
-
- /* Try to add any repo-specific reserved names - the gitlink file
- * within a submodule or the repository (if the repository directory
- * is beneath the workdir). These are typically `.git`, but should
- * be protected in case they are not. Note, repo and workdir paths
- * are always prettified to end in `/`, so a prefixcmp is safe.
- */
- if (!repo->is_bare) {
- int (*prefixcmp)(const char *, const char *);
- int error, ignorecase;
-
- error = git_repository__cvar(
- &ignorecase, repo, GIT_CVAR_IGNORECASE);
- prefixcmp = (error || ignorecase) ? git__prefixcmp_icase :
- git__prefixcmp;
-
- if (repo->path_gitlink &&
- reserved_names_add8dot3(repo, repo->path_gitlink) < 0)
- goto on_error;
-
- if (repo->path_repository &&
- prefixcmp(repo->path_repository, repo->workdir) == 0 &&
- reserved_names_add8dot3(repo, repo->path_repository) < 0)
- goto on_error;
- }
- }
-
- *out = repo->reserved_names.ptr;
- *outlen = repo->reserved_names.size;
-
- return true;
-
- /* Always give good defaults, even on OOM */
-on_error:
- *out = git_repository__reserved_names_win32;
- *outlen = git_repository__reserved_names_win32_len;
-
- return false;
-}
-#else
-bool git_repository__reserved_names(
- git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs)
-{
- GIT_UNUSED(repo);
-
- if (include_ntfs) {
- *out = git_repository__reserved_names_win32;
- *outlen = git_repository__reserved_names_win32_len;
- } else {
- *out = git_repository__reserved_names_posix;
- *outlen = git_repository__reserved_names_posix_len;
- }
-
- return true;
-}
-#endif
-
-static int check_repositoryformatversion(git_config *config)
-{
- int version, error;
-
- error = git_config_get_int32(&version, config, "core.repositoryformatversion");
- /* git ignores this if the config variable isn't there */
- if (error == GIT_ENOTFOUND)
- return 0;
-
- if (error < 0)
- return -1;
-
- if (GIT_REPO_VERSION < version) {
- giterr_set(GITERR_REPOSITORY,
- "Unsupported repository version %d. Only versions up to %d are supported.",
- version, GIT_REPO_VERSION);
- return -1;
- }
-
- return 0;
-}
-
-static int repo_init_create_head(const char *git_dir, const char *ref_name)
-{
- git_buf ref_path = GIT_BUF_INIT;
- git_filebuf ref = GIT_FILEBUF_INIT;
- const char *fmt;
-
- if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
- git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE) < 0)
- goto fail;
-
- if (!ref_name)
- ref_name = GIT_BRANCH_MASTER;
-
- if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0)
- fmt = "ref: %s\n";
- else
- fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n";
-
- if (git_filebuf_printf(&ref, fmt, ref_name) < 0 ||
- git_filebuf_commit(&ref) < 0)
- goto fail;
-
- git_buf_free(&ref_path);
- return 0;
-
-fail:
- git_buf_free(&ref_path);
- git_filebuf_cleanup(&ref);
- return -1;
-}
-
-static bool is_chmod_supported(const char *file_path)
-{
- struct stat st1, st2;
-
- if (p_stat(file_path, &st1) < 0)
- return false;
-
- if (p_chmod(file_path, st1.st_mode ^ S_IXUSR) < 0)
- return false;
-
- if (p_stat(file_path, &st2) < 0)
- return false;
-
- return (st1.st_mode != st2.st_mode);
-}
-
-static bool is_filesystem_case_insensitive(const char *gitdir_path)
-{
- git_buf path = GIT_BUF_INIT;
- int is_insensitive = -1;
-
- if (!git_buf_joinpath(&path, gitdir_path, "CoNfIg"))
- is_insensitive = git_path_exists(git_buf_cstr(&path));
-
- git_buf_free(&path);
- return is_insensitive;
-}
-
-static bool are_symlinks_supported(const char *wd_path)
-{
- git_buf path = GIT_BUF_INIT;
- int fd;
- struct stat st;
- int symlinks_supported = -1;
-
- if ((fd = git_futils_mktmp(&path, wd_path, 0666)) < 0 ||
- p_close(fd) < 0 ||
- p_unlink(path.ptr) < 0 ||
- p_symlink("testing", path.ptr) < 0 ||
- p_lstat(path.ptr, &st) < 0)
- symlinks_supported = false;
- else
- symlinks_supported = (S_ISLNK(st.st_mode) != 0);
-
- (void)p_unlink(path.ptr);
- git_buf_free(&path);
-
- return symlinks_supported;
-}
-
-static int create_empty_file(const char *path, mode_t mode)
-{
- int fd;
-
- if ((fd = p_creat(path, mode)) < 0) {
- giterr_set(GITERR_OS, "Error while creating '%s'", path);
- return -1;
- }
-
- if (p_close(fd) < 0) {
- giterr_set(GITERR_OS, "Error while closing '%s'", path);
- return -1;
- }
-
- return 0;
-}
-
-static int repo_local_config(
- git_config **out,
- git_buf *config_dir,
- git_repository *repo,
- const char *repo_dir)
-{
- int error = 0;
- git_config *parent;
- const char *cfg_path;
-
- if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
- return -1;
- cfg_path = git_buf_cstr(config_dir);
-
- /* make LOCAL config if missing */
- if (!git_path_isfile(cfg_path) &&
- (error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
- return error;
-
- /* if no repo, just open that file directly */
- if (!repo)
- return git_config_open_ondisk(out, cfg_path);
-
- /* otherwise, open parent config and get that level */
- if ((error = git_repository_config__weakptr(&parent, repo)) < 0)
- return error;
-
- if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) {
- giterr_clear();
-
- if (!(error = git_config_add_file_ondisk(
- parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
- error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
- }
-
- git_config_free(parent);
-
- return error;
-}
-
-static int repo_init_fs_configs(
- git_config *cfg,
- const char *cfg_path,
- const char *repo_dir,
- const char *work_dir,
- bool update_ignorecase)
-{
- int error = 0;
-
- if (!work_dir)
- work_dir = repo_dir;
-
- if ((error = git_config_set_bool(
- cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
- return error;
-
- if (!are_symlinks_supported(work_dir)) {
- if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
- return error;
- } else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
- giterr_clear();
-
- if (update_ignorecase) {
- if (is_filesystem_case_insensitive(repo_dir)) {
- if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0)
- return error;
- } else if (git_config_delete_entry(cfg, "core.ignorecase") < 0)
- giterr_clear();
- }
-
-#ifdef GIT_USE_ICONV
- if ((error = git_config_set_bool(
- cfg, "core.precomposeunicode",
- git_path_does_fs_decompose_unicode(work_dir))) < 0)
- return error;
- /* on non-iconv platforms, don't even set core.precomposeunicode */
-#endif
-
- return 0;
-}
-
-static int repo_init_config(
- const char *repo_dir,
- const char *work_dir,
- uint32_t flags,
- uint32_t mode)
-{
- int error = 0;
- git_buf cfg_path = GIT_BUF_INIT, worktree_path = GIT_BUF_INIT;
- git_config *config = NULL;
- bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
- bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
-
- if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
- goto cleanup;
-
- if (is_reinit && (error = check_repositoryformatversion(config)) < 0)
- goto cleanup;
-
-#define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \
- if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
- goto cleanup; } while (0)
-
- SET_REPO_CONFIG(bool, "core.bare", is_bare);
- SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
-
- if ((error = repo_init_fs_configs(
- config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
- goto cleanup;
-
- if (!is_bare) {
- SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
-
- if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
- if ((error = git_buf_sets(&worktree_path, work_dir)) < 0)
- goto cleanup;
-
- if ((flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK))
- if ((error = git_path_make_relative(&worktree_path, repo_dir)) < 0)
- goto cleanup;
-
- SET_REPO_CONFIG(string, "core.worktree", worktree_path.ptr);
- } else if (is_reinit) {
- if (git_config_delete_entry(config, "core.worktree") < 0)
- giterr_clear();
- }
- }
-
- if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
- SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
- SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
- }
- else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
- SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
- SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
- }
-
-cleanup:
- git_buf_free(&cfg_path);
- git_buf_free(&worktree_path);
- git_config_free(config);
-
- return error;
-}
-
-static int repo_reinit_submodule_fs(git_submodule *sm, const char *n, void *p)
-{
- git_repository *smrepo = NULL;
- GIT_UNUSED(n); GIT_UNUSED(p);
-
- if (git_submodule_open(&smrepo, sm) < 0 ||
- git_repository_reinit_filesystem(smrepo, true) < 0)
- giterr_clear();
- git_repository_free(smrepo);
-
- return 0;
-}
-
-int git_repository_reinit_filesystem(git_repository *repo, int recurse)
-{
- int error = 0;
- git_buf path = GIT_BUF_INIT;
- git_config *config = NULL;
- const char *repo_dir = git_repository_path(repo);
-
- if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
- error = repo_init_fs_configs(
- config, path.ptr, repo_dir, git_repository_workdir(repo), true);
-
- git_config_free(config);
- git_buf_free(&path);
-
- git_repository__cvar_cache_clear(repo);
-
- if (!repo->is_bare && recurse)
- (void)git_submodule_foreach(repo, repo_reinit_submodule_fs, NULL);
-
- return error;
-}
-
-static int repo_write_template(
- const char *git_dir,
- bool allow_overwrite,
- const char *file,
- mode_t mode,
- bool hidden,
- const char *content)
-{
- git_buf path = GIT_BUF_INIT;
- int fd, error = 0, flags;
-
- if (git_buf_joinpath(&path, git_dir, file) < 0)
- return -1;
-
- if (allow_overwrite)
- flags = O_WRONLY | O_CREAT | O_TRUNC;
- else
- flags = O_WRONLY | O_CREAT | O_EXCL;
-
- fd = p_open(git_buf_cstr(&path), flags, mode);
-
- if (fd >= 0) {
- error = p_write(fd, content, strlen(content));
-
- p_close(fd);
- }
- else if (errno != EEXIST)
- error = fd;
-
-#ifdef GIT_WIN32
- if (!error && hidden) {
- if (git_win32__set_hidden(path.ptr, true) < 0)
- error = -1;
- }
-#else
- GIT_UNUSED(hidden);
-#endif
-
- git_buf_free(&path);
-
- if (error)
- giterr_set(GITERR_OS,
- "Failed to initialize repository with template '%s'", file);
-
- return error;
-}
-
-static int repo_write_gitlink(
- const char *in_dir, const char *to_repo, bool use_relative_path)
-{
- int error;
- git_buf buf = GIT_BUF_INIT;
- git_buf path_to_repo = GIT_BUF_INIT;
- struct stat st;
-
- git_path_dirname_r(&buf, to_repo);
- git_path_to_dir(&buf);
- if (git_buf_oom(&buf))
- return -1;
-
- /* don't write gitlink to natural workdir */
- if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 &&
- strcmp(in_dir, buf.ptr) == 0)
- {
- error = GIT_PASSTHROUGH;
- goto cleanup;
- }
-
- if ((error = git_buf_joinpath(&buf, in_dir, DOT_GIT)) < 0)
- goto cleanup;
-
- if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
- giterr_set(GITERR_REPOSITORY,
- "Cannot overwrite gitlink file into path '%s'", in_dir);
- error = GIT_EEXISTS;
- goto cleanup;
- }
-
- git_buf_clear(&buf);
-
- error = git_buf_sets(&path_to_repo, to_repo);
-
- if (!error && use_relative_path)
- error = git_path_make_relative(&path_to_repo, in_dir);
-
- if (!error)
- error = git_buf_join(&buf, ' ', GIT_FILE_CONTENT_PREFIX, path_to_repo.ptr);
-
- if (!error)
- error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr);
-
-cleanup:
- git_buf_free(&buf);
- git_buf_free(&path_to_repo);
- return error;
-}
-
-static mode_t pick_dir_mode(git_repository_init_options *opts)
-{
- if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
- return 0777;
- if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
- return (0775 | S_ISGID);
- if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL)
- return (0777 | S_ISGID);
- return opts->mode;
-}
-
-#include "repo_template.h"
-
-static int repo_init_structure(
- const char *repo_dir,
- const char *work_dir,
- git_repository_init_options *opts)
-{
- int error = 0;
- repo_template_item *tpl;
- bool external_tpl =
- ((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
- mode_t dmode = pick_dir_mode(opts);
- bool chmod = opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK;
-
- /* Hide the ".git" directory */
-#ifdef GIT_WIN32
- if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) {
- if (git_win32__set_hidden(repo_dir, true) < 0) {
- giterr_set(GITERR_OS,
- "Failed to mark Git repository folder as hidden");
- return -1;
- }
- }
-#endif
-
- /* Create the .git gitlink if appropriate */
- if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
- (opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
- {
- if (repo_write_gitlink(work_dir, repo_dir, opts->flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK) < 0)
- return -1;
- }
-
- /* Copy external template if requested */
- if (external_tpl) {
- git_config *cfg = NULL;
- const char *tdir = NULL;
- bool default_template = false;
- git_buf template_buf = GIT_BUF_INIT;
-
- if (opts->template_path)
- tdir = opts->template_path;
- else if ((error = git_config_open_default(&cfg)) >= 0) {
- if (!git_config_get_path(&template_buf, cfg, "init.templatedir"))
- tdir = template_buf.ptr;
- giterr_clear();
- }
-
- if (!tdir) {
- if (!(error = git_sysdir_find_template_dir(&template_buf)))
- tdir = template_buf.ptr;
- default_template = true;
- }
-
- if (tdir) {
- uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS |
- GIT_CPDIR_SIMPLE_TO_MODE |
- GIT_CPDIR_COPY_DOTFILES;
- if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK)
- cpflags |= GIT_CPDIR_CHMOD_DIRS;
- error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode);
- }
-
- git_buf_free(&template_buf);
- git_config_free(cfg);
-
- if (error < 0) {
- if (!default_template)
- return error;
-
- /* if template was default, ignore error and use internal */
- giterr_clear();
- external_tpl = false;
- error = 0;
- }
- }
-
- /* Copy internal template
- * - always ensure existence of dirs
- * - only create files if no external template was specified
- */
- for (tpl = repo_template; !error && tpl->path; ++tpl) {
- if (!tpl->content) {
- uint32_t mkdir_flags = GIT_MKDIR_PATH;
- if (chmod)
- mkdir_flags |= GIT_MKDIR_CHMOD;
-
- error = git_futils_mkdir_relative(
- tpl->path, repo_dir, dmode, mkdir_flags, NULL);
- }
- else if (!external_tpl) {
- const char *content = tpl->content;
-
- if (opts->description && strcmp(tpl->path, GIT_DESC_FILE) == 0)
- content = opts->description;
-
- error = repo_write_template(
- repo_dir, false, tpl->path, tpl->mode, false, content);
- }
- }
-
- return error;
-}
-
-static int mkdir_parent(git_buf *buf, uint32_t mode, bool skip2)
-{
- /* When making parent directories during repository initialization
- * don't try to set gid or grant world write access
- */
- return git_futils_mkdir(
- buf->ptr, mode & ~(S_ISGID | 0002),
- GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR |
- (skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST));
-}
-
-static int repo_init_directories(
- git_buf *repo_path,
- git_buf *wd_path,
- const char *given_repo,
- git_repository_init_options *opts)
-{
- int error = 0;
- bool is_bare, add_dotgit, has_dotgit, natural_wd;
- mode_t dirmode;
-
- /* There are three possible rules for what we are allowed to create:
- * - MKPATH means anything we need
- * - MKDIR means just the .git directory and its parent and the workdir
- * - Neither means only the .git directory can be created
- *
- * There are 5 "segments" of path that we might need to deal with:
- * 1. The .git directory
- * 2. The parent of the .git directory
- * 3. Everything above the parent of the .git directory
- * 4. The working directory (often the same as #2)
- * 5. Everything above the working directory (often the same as #3)
- *
- * For all directories created, we start with the init_mode value for
- * permissions and then strip off bits in some cases:
- *
- * For MKPATH, we create #3 (and #5) paths without S_ISGID or S_IWOTH
- * For MKPATH and MKDIR, we create #2 (and #4) without S_ISGID
- * For all rules, we create #1 using the untouched init_mode
- */
-
- /* set up repo path */
-
- is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
-
- add_dotgit =
- (opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 &&
- !is_bare &&
- git__suffixcmp(given_repo, "/" DOT_GIT) != 0 &&
- git__suffixcmp(given_repo, "/" GIT_DIR) != 0;
-
- if (git_buf_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
- return -1;
-
- has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
- if (has_dotgit)
- opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;
-
- /* set up workdir path */
-
- if (!is_bare) {
- if (opts->workdir_path) {
- if (git_path_join_unrooted(
- wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
- return -1;
- } else if (has_dotgit) {
- if (git_path_dirname_r(wd_path, repo_path->ptr) < 0)
- return -1;
- } else {
- giterr_set(GITERR_REPOSITORY, "Cannot pick working directory"
- " for non-bare repository that isn't a '.git' directory");
- return -1;
- }
-
- if (git_path_to_dir(wd_path) < 0)
- return -1;
- } else {
- git_buf_clear(wd_path);
- }
-
- natural_wd =
- has_dotgit &&
- wd_path->size > 0 &&
- wd_path->size + strlen(GIT_DIR) == repo_path->size &&
- memcmp(repo_path->ptr, wd_path->ptr, wd_path->size) == 0;
- if (natural_wd)
- opts->flags |= GIT_REPOSITORY_INIT__NATURAL_WD;
-
- /* create directories as needed / requested */
-
- dirmode = pick_dir_mode(opts);
-
- if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0) {
- /* create path #5 */
- if (wd_path->size > 0 &&
- (error = mkdir_parent(wd_path, dirmode, false)) < 0)
- return error;
-
- /* create path #3 (if not the same as #5) */
- if (!natural_wd &&
- (error = mkdir_parent(repo_path, dirmode, has_dotgit)) < 0)
- return error;
- }
-
- if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
- (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0)
- {
- /* create path #4 */
- if (wd_path->size > 0 &&
- (error = git_futils_mkdir(
- wd_path->ptr, dirmode & ~S_ISGID,
- GIT_MKDIR_VERIFY_DIR)) < 0)
- return error;
-
- /* create path #2 (if not the same as #4) */
- if (!natural_wd &&
- (error = git_futils_mkdir(
- repo_path->ptr, dirmode & ~S_ISGID,
- GIT_MKDIR_VERIFY_DIR | GIT_MKDIR_SKIP_LAST)) < 0)
- return error;
- }
-
- if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
- (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
- has_dotgit)
- {
- /* create path #1 */
- error = git_futils_mkdir(repo_path->ptr, dirmode,
- GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0));
- }
-
- /* prettify both directories now that they are created */
-
- if (!error) {
- error = git_path_prettify_dir(repo_path, repo_path->ptr, NULL);
-
- if (!error && wd_path->size > 0)
- error = git_path_prettify_dir(wd_path, wd_path->ptr, NULL);
- }
-
- return error;
-}
-
-static int repo_init_create_origin(git_repository *repo, const char *url)
-{
- int error;
- git_remote *remote;
-
- if (!(error = git_remote_create(&remote, repo, GIT_REMOTE_ORIGIN, url))) {
- git_remote_free(remote);
- }
-
- return error;
-}
-
-int git_repository_init(
- git_repository **repo_out, const char *path, unsigned is_bare)
-{
- git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
-
- opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't love this default */
- if (is_bare)
- opts.flags |= GIT_REPOSITORY_INIT_BARE;
-
- return git_repository_init_ext(repo_out, path, &opts);
-}
-
-int git_repository_init_ext(
- git_repository **out,
- const char *given_repo,
- git_repository_init_options *opts)
-{
- int error;
- git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT;
- const char *wd;
-
- assert(out && given_repo && opts);
-
- GITERR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options");
-
- error = repo_init_directories(&repo_path, &wd_path, given_repo, opts);
- if (error < 0)
- goto cleanup;
-
- wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_buf_cstr(&wd_path);
- if (valid_repository_path(&repo_path)) {
-
- if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
- giterr_set(GITERR_REPOSITORY,
- "Attempt to reinitialize '%s'", given_repo);
- error = GIT_EEXISTS;
- goto cleanup;
- }
-
- opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
-
- error = repo_init_config(
- repo_path.ptr, wd, opts->flags, opts->mode);
-
- /* TODO: reinitialize the templates */
- }
- else {
- if (!(error = repo_init_structure(
- repo_path.ptr, wd, opts)) &&
- !(error = repo_init_config(
- repo_path.ptr, wd, opts->flags, opts->mode)))
- error = repo_init_create_head(
- repo_path.ptr, opts->initial_head);
- }
- if (error < 0)
- goto cleanup;
-
- error = git_repository_open(out, repo_path.ptr);
-
- if (!error && opts->origin_url)
- error = repo_init_create_origin(*out, opts->origin_url);
-
-cleanup:
- git_buf_free(&repo_path);
- git_buf_free(&wd_path);
-
- return error;
-}
-
-int git_repository_head_detached(git_repository *repo)
-{
- git_reference *ref;
- git_odb *odb = NULL;
- int exists;
-
- if (git_repository_odb__weakptr(&odb, repo) < 0)
- return -1;
-
- if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
- return -1;
-
- if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
- git_reference_free(ref);
- return 0;
- }
-
- exists = git_odb_exists(odb, git_reference_target(ref));
-
- git_reference_free(ref);
- return exists;
-}
-
-int git_repository_head(git_reference **head_out, git_repository *repo)
-{
- git_reference *head;
- int error;
-
- if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
- return error;
-
- if (git_reference_type(head) == GIT_REF_OID) {
- *head_out = head;
- return 0;
- }
-
- error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1);
- git_reference_free(head);
-
- return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
-}
-
-int git_repository_head_unborn(git_repository *repo)
-{
- git_reference *ref = NULL;
- int error;
-
- error = git_repository_head(&ref, repo);
- git_reference_free(ref);
-
- if (error == GIT_EUNBORNBRANCH) {
- giterr_clear();
- return 1;
- }
-
- if (error < 0)
- return -1;
-
- return 0;
-}
-
-static int at_least_one_cb(const char *refname, void *payload)
-{
- GIT_UNUSED(refname);
- GIT_UNUSED(payload);
- return GIT_PASSTHROUGH;
-}
-
-static int repo_contains_no_reference(git_repository *repo)
-{
- int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL);
-
- if (error == GIT_PASSTHROUGH)
- return 0;
-
- if (!error)
- return 1;
-
- return error;
-}
-
-int git_repository_is_empty(git_repository *repo)
-{
- git_reference *head = NULL;
- int is_empty = 0;
-
- if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
- return -1;
-
- if (git_reference_type(head) == GIT_REF_SYMBOLIC)
- is_empty =
- (strcmp(git_reference_symbolic_target(head),
- GIT_REFS_HEADS_DIR "master") == 0) &&
- repo_contains_no_reference(repo);
-
- git_reference_free(head);
-
- return is_empty;
-}
-
-const char *git_repository_path(git_repository *repo)
-{
- assert(repo);
- return repo->path_repository;
-}
-
-const char *git_repository_workdir(git_repository *repo)
-{
- assert(repo);
-
- if (repo->is_bare)
- return NULL;
-
- return repo->workdir;
-}
-
-int git_repository_set_workdir(
- git_repository *repo, const char *workdir, int update_gitlink)
-{
- int error = 0;
- git_buf path = GIT_BUF_INIT;
-
- assert(repo && workdir);
-
- if (git_path_prettify_dir(&path, workdir, NULL) < 0)
- return -1;
-
- if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0)
- return 0;
-
- if (update_gitlink) {
- git_config *config;
-
- if (git_repository_config__weakptr(&config, repo) < 0)
- return -1;
-
- error = repo_write_gitlink(path.ptr, git_repository_path(repo), false);
-
- /* passthrough error means gitlink is unnecessary */
- if (error == GIT_PASSTHROUGH)
- error = git_config_delete_entry(config, "core.worktree");
- else if (!error)
- error = git_config_set_string(config, "core.worktree", path.ptr);
-
- if (!error)
- error = git_config_set_bool(config, "core.bare", false);
- }
-
- if (!error) {
- char *old_workdir = repo->workdir;
-
- repo->workdir = git_buf_detach(&path);
- repo->is_bare = 0;
-
- git__free(old_workdir);
- }
-
- return error;
-}
-
-int git_repository_is_bare(git_repository *repo)
-{
- assert(repo);
- return repo->is_bare;
-}
-
-int git_repository_set_bare(git_repository *repo)
-{
- int error;
- git_config *config;
-
- assert(repo);
-
- if (repo->is_bare)
- return 0;
-
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
- return error;
-
- if ((error = git_config_set_bool(config, "core.bare", true)) < 0)
- return error;
-
- if ((error = git_config__update_entry(config, "core.worktree", NULL, true, true)) < 0)
- return error;
-
- git__free(repo->workdir);
- repo->workdir = NULL;
- repo->is_bare = 1;
-
- return 0;
-}
-
-int git_repository_head_tree(git_tree **tree, git_repository *repo)
-{
- git_reference *head;
- git_object *obj;
- int error;
-
- if ((error = git_repository_head(&head, repo)) < 0)
- return error;
-
- if ((error = git_reference_peel(&obj, head, GIT_OBJ_TREE)) < 0)
- goto cleanup;
-
- *tree = (git_tree *)obj;
-
-cleanup:
- git_reference_free(head);
- return error;
-}
-
-int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf file_path = GIT_BUF_INIT;
- char orig_head_str[GIT_OID_HEXSZ];
- int error = 0;
-
- git_oid_fmt(orig_head_str, orig_head);
-
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 &&
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 &&
- (error = git_filebuf_printf(&file, "%.*s\n", GIT_OID_HEXSZ, orig_head_str)) == 0)
- error = git_filebuf_commit(&file);
-
- if (error < 0)
- git_filebuf_cleanup(&file);
-
- git_buf_free(&file_path);
-
- return error;
-}
-
-int git_repository_message(git_buf *out, git_repository *repo)
-{
- git_buf path = GIT_BUF_INIT;
- struct stat st;
- int error;
-
- git_buf_sanitize(out);
-
- if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
- return -1;
-
- if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) {
- if (errno == ENOENT)
- error = GIT_ENOTFOUND;
- giterr_set(GITERR_OS, "Could not access message file");
- } else {
- error = git_futils_readbuffer(out, git_buf_cstr(&path));
- }
-
- git_buf_free(&path);
-
- return error;
-}
-
-int git_repository_message_remove(git_repository *repo)
-{
- git_buf path = GIT_BUF_INIT;
- int error;
-
- if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
- return -1;
-
- error = p_unlink(git_buf_cstr(&path));
- git_buf_free(&path);
-
- return error;
-}
-
-int git_repository_hashfile(
- git_oid *out,
- git_repository *repo,
- const char *path,
- git_otype type,
- const char *as_path)
-{
- int error;
- git_filter_list *fl = NULL;
- git_file fd = -1;
- git_off_t len;
- git_buf full_path = GIT_BUF_INIT;
-
- assert(out && path && repo); /* as_path can be NULL */
-
- /* At some point, it would be nice if repo could be NULL to just
- * apply filter rules defined in system and global files, but for
- * now that is not possible because git_filters_load() needs it.
- */
-
- error = git_path_join_unrooted(
- &full_path, path, git_repository_workdir(repo), NULL);
- if (error < 0)
- return error;
-
- if (!as_path)
- as_path = path;
-
- /* passing empty string for "as_path" indicated --no-filters */
- if (strlen(as_path) > 0) {
- error = git_filter_list_load(
- &fl, repo, NULL, as_path,
- GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT);
- if (error < 0)
- return error;
- } else {
- error = 0;
- }
-
- /* at this point, error is a count of the number of loaded filters */
-
- fd = git_futils_open_ro(full_path.ptr);
- if (fd < 0) {
- error = fd;
- goto cleanup;
- }
-
- len = git_futils_filesize(fd);
- if (len < 0) {
- error = (int)len;
- goto cleanup;
- }
-
- if (!git__is_sizet(len)) {
- giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
- error = -1;
- goto cleanup;
- }
-
- error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, fl);
-
-cleanup:
- if (fd >= 0)
- p_close(fd);
- git_filter_list_free(fl);
- git_buf_free(&full_path);
-
- return error;
-}
-
-static int checkout_message(git_buf *out, git_reference *old, const char *new)
-{
- git_buf_puts(out, "checkout: moving from ");
-
- if (git_reference_type(old) == GIT_REF_SYMBOLIC)
- git_buf_puts(out, git_reference__shorthand(git_reference_symbolic_target(old)));
- else
- git_buf_puts(out, git_oid_tostr_s(git_reference_target(old)));
-
- git_buf_puts(out, " to ");
-
- if (git_reference__is_branch(new))
- git_buf_puts(out, git_reference__shorthand(new));
- else
- git_buf_puts(out, new);
-
- if (git_buf_oom(out))
- return -1;
-
- return 0;
-}
-
-int git_repository_set_head(
- git_repository* repo,
- const char* refname)
-{
- git_reference *ref = NULL, *current = NULL, *new_head = NULL;
- git_buf log_message = GIT_BUF_INIT;
- int error;
-
- assert(repo && refname);
-
- if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0)
- return error;
-
- if ((error = checkout_message(&log_message, current, refname)) < 0)
- goto cleanup;
-
- error = git_reference_lookup(&ref, repo, refname);
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- if (!error) {
- if (git_reference_is_branch(ref)) {
- error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE,
- git_reference_name(ref), true, git_buf_cstr(&log_message));
- } else {
- error = git_repository_set_head_detached(repo, git_reference_target(ref));
- }
- } else if (git_reference__is_branch(refname)) {
- error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname,
- true, git_buf_cstr(&log_message));
- }
-
-cleanup:
- git_buf_free(&log_message);
- git_reference_free(current);
- git_reference_free(ref);
- git_reference_free(new_head);
- return error;
-}
-
-static int detach(git_repository *repo, const git_oid *id, const char *from)
-{
- int error;
- git_buf log_message = GIT_BUF_INIT;
- git_object *object = NULL, *peeled = NULL;
- git_reference *new_head = NULL, *current = NULL;
-
- assert(repo && id);
-
- if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0)
- return error;
-
- if ((error = git_object_lookup(&object, repo, id, GIT_OBJ_ANY)) < 0)
- goto cleanup;
-
- if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0)
- goto cleanup;
-
- if (from == NULL)
- from = git_oid_tostr_s(git_object_id(peeled));
-
- if ((error = checkout_message(&log_message, current, from)) < 0)
- goto cleanup;
-
- error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message));
-
-cleanup:
- git_buf_free(&log_message);
- git_object_free(object);
- git_object_free(peeled);
- git_reference_free(current);
- git_reference_free(new_head);
- return error;
-}
-
-int git_repository_set_head_detached(
- git_repository* repo,
- const git_oid* commitish)
-{
- return detach(repo, commitish, NULL);
-}
-
-int git_repository_set_head_detached_from_annotated(
- git_repository *repo,
- const git_annotated_commit *commitish)
-{
- assert(repo && commitish);
-
- return detach(repo, git_annotated_commit_id(commitish), commitish->description);
-}
-
-int git_repository_detach_head(git_repository* repo)
-{
- git_reference *old_head = NULL, *new_head = NULL, *current = NULL;
- git_object *object = NULL;
- git_buf log_message = GIT_BUF_INIT;
- int error;
-
- assert(repo);
-
- if ((error = git_reference_lookup(¤t, repo, GIT_HEAD_FILE)) < 0)
- return error;
-
- if ((error = git_repository_head(&old_head, repo)) < 0)
- goto cleanup;
-
- if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJ_COMMIT)) < 0)
- goto cleanup;
-
- if ((error = checkout_message(&log_message, current, git_oid_tostr_s(git_object_id(object)))) < 0)
- goto cleanup;
-
- error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head),
- 1, git_buf_cstr(&log_message));
-
-cleanup:
- git_buf_free(&log_message);
- git_object_free(object);
- git_reference_free(old_head);
- git_reference_free(new_head);
- git_reference_free(current);
- return error;
-}
-
-/**
- * Loosely ported from git.git
- * https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh#L198-289
- */
-int git_repository_state(git_repository *repo)
-{
- git_buf repo_path = GIT_BUF_INIT;
- int state = GIT_REPOSITORY_STATE_NONE;
-
- assert(repo);
-
- if (git_buf_puts(&repo_path, repo->path_repository) < 0)
- return -1;
-
- if (git_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE))
- state = GIT_REPOSITORY_STATE_REBASE_INTERACTIVE;
- else if (git_path_contains_dir(&repo_path, GIT_REBASE_MERGE_DIR))
- state = GIT_REPOSITORY_STATE_REBASE_MERGE;
- else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_REBASING_FILE))
- state = GIT_REPOSITORY_STATE_REBASE;
- else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_APPLYING_FILE))
- state = GIT_REPOSITORY_STATE_APPLY_MAILBOX;
- else if (git_path_contains_dir(&repo_path, GIT_REBASE_APPLY_DIR))
- state = GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE;
- else if (git_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE))
- state = GIT_REPOSITORY_STATE_MERGE;
- else if (git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) {
- state = GIT_REPOSITORY_STATE_REVERT;
- if (git_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) {
- state = GIT_REPOSITORY_STATE_REVERT_SEQUENCE;
- }
- } else if (git_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE)) {
- state = GIT_REPOSITORY_STATE_CHERRYPICK;
- if (git_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) {
- state = GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE;
- }
- } else if (git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE))
- state = GIT_REPOSITORY_STATE_BISECT;
-
- git_buf_free(&repo_path);
- return state;
-}
-
-int git_repository__cleanup_files(
- git_repository *repo, const char *files[], size_t files_len)
-{
- git_buf buf = GIT_BUF_INIT;
- size_t i;
- int error;
-
- for (error = 0, i = 0; !error && i < files_len; ++i) {
- const char *path;
-
- if (git_buf_joinpath(&buf, repo->path_repository, files[i]) < 0)
- return -1;
-
- path = git_buf_cstr(&buf);
-
- if (git_path_isfile(path)) {
- error = p_unlink(path);
- } else if (git_path_isdir(path)) {
- error = git_futils_rmdir_r(path, NULL,
- GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS);
- }
-
- git_buf_clear(&buf);
- }
-
- git_buf_free(&buf);
- return error;
-}
-
-static const char *state_files[] = {
- GIT_MERGE_HEAD_FILE,
- GIT_MERGE_MODE_FILE,
- GIT_MERGE_MSG_FILE,
- GIT_REVERT_HEAD_FILE,
- GIT_CHERRYPICK_HEAD_FILE,
- GIT_BISECT_LOG_FILE,
- GIT_REBASE_MERGE_DIR,
- GIT_REBASE_APPLY_DIR,
- GIT_SEQUENCER_DIR,
-};
-
-int git_repository_state_cleanup(git_repository *repo)
-{
- assert(repo);
-
- return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
-}
-
-int git_repository_is_shallow(git_repository *repo)
-{
- git_buf path = GIT_BUF_INIT;
- struct stat st;
- int error;
-
- if ((error = git_buf_joinpath(&path, repo->path_repository, "shallow")) < 0)
- return error;
-
- error = git_path_lstat(path.ptr, &st);
- git_buf_free(&path);
-
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- return 0;
- }
-
- if (error < 0)
- return error;
- return st.st_size == 0 ? 0 : 1;
-}
-
-int git_repository_init_init_options(
- git_repository_init_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_repository_init_options,
- GIT_REPOSITORY_INIT_OPTIONS_INIT);
- return 0;
-}
-
-int git_repository_ident(const char **name, const char **email, const git_repository *repo)
-{
- *name = repo->ident_name;
- *email = repo->ident_email;
-
- return 0;
-}
-
-int git_repository_set_ident(git_repository *repo, const char *name, const char *email)
-{
- char *tmp_name = NULL, *tmp_email = NULL;
-
- if (name) {
- tmp_name = git__strdup(name);
- GITERR_CHECK_ALLOC(tmp_name);
- }
-
- if (email) {
- tmp_email = git__strdup(email);
- GITERR_CHECK_ALLOC(tmp_email);
- }
-
- tmp_name = git__swap(repo->ident_name, tmp_name);
- tmp_email = git__swap(repo->ident_email, tmp_email);
-
- git__free(tmp_name);
- git__free(tmp_email);
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_repository_h__
-#define INCLUDE_repository_h__
-
-#include "git2/common.h"
-#include "git2/oid.h"
-#include "git2/odb.h"
-#include "git2/repository.h"
-#include "git2/object.h"
-#include "git2/config.h"
-
-#include "array.h"
-#include "cache.h"
-#include "refs.h"
-#include "buffer.h"
-#include "object.h"
-#include "attrcache.h"
-#include "submodule.h"
-#include "diff_driver.h"
-
-#define DOT_GIT ".git"
-#define GIT_DIR DOT_GIT "/"
-#define GIT_DIR_MODE 0755
-#define GIT_BARE_DIR_MODE 0777
-
-/* Default DOS-compatible 8.3 "short name" for a git repository, "GIT~1" */
-#define GIT_DIR_SHORTNAME "GIT~1"
-
-/** Cvar cache identifiers */
-typedef enum {
- GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */
- GIT_CVAR_EOL, /* core.eol */
- GIT_CVAR_SYMLINKS, /* core.symlinks */
- GIT_CVAR_IGNORECASE, /* core.ignorecase */
- GIT_CVAR_FILEMODE, /* core.filemode */
- GIT_CVAR_IGNORESTAT, /* core.ignorestat */
- GIT_CVAR_TRUSTCTIME, /* core.trustctime */
- GIT_CVAR_ABBREV, /* core.abbrev */
- GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
- GIT_CVAR_SAFE_CRLF, /* core.safecrlf */
- GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */
- GIT_CVAR_PROTECTHFS, /* core.protectHFS */
- GIT_CVAR_PROTECTNTFS, /* core.protectNTFS */
- GIT_CVAR_CACHE_MAX
-} git_cvar_cached;
-
-/**
- * CVAR value enumerations
- *
- * These are the values that are actually stored in the cvar cache, instead
- * of their string equivalents. These values are internal and symbolic;
- * make sure that none of them is set to `-1`, since that is the unique
- * identifier for "not cached"
- */
-typedef enum {
- /* The value hasn't been loaded from the cache yet */
- GIT_CVAR_NOT_CACHED = -1,
-
- /* core.safecrlf: false, 'fail', 'warn' */
- GIT_SAFE_CRLF_FALSE = 0,
- GIT_SAFE_CRLF_FAIL = 1,
- GIT_SAFE_CRLF_WARN = 2,
-
- /* core.autocrlf: false, true, 'input; */
- GIT_AUTO_CRLF_FALSE = 0,
- GIT_AUTO_CRLF_TRUE = 1,
- GIT_AUTO_CRLF_INPUT = 2,
- GIT_AUTO_CRLF_DEFAULT = GIT_AUTO_CRLF_FALSE,
-
- /* core.eol: unset, 'crlf', 'lf', 'native' */
- GIT_EOL_UNSET = 0,
- GIT_EOL_CRLF = 1,
- GIT_EOL_LF = 2,
-#ifdef GIT_WIN32
- GIT_EOL_NATIVE = GIT_EOL_CRLF,
-#else
- GIT_EOL_NATIVE = GIT_EOL_LF,
-#endif
- GIT_EOL_DEFAULT = GIT_EOL_NATIVE,
-
- /* core.symlinks: bool */
- GIT_SYMLINKS_DEFAULT = GIT_CVAR_TRUE,
- /* core.ignorecase */
- GIT_IGNORECASE_DEFAULT = GIT_CVAR_FALSE,
- /* core.filemode */
- GIT_FILEMODE_DEFAULT = GIT_CVAR_TRUE,
- /* core.ignorestat */
- GIT_IGNORESTAT_DEFAULT = GIT_CVAR_FALSE,
- /* core.trustctime */
- GIT_TRUSTCTIME_DEFAULT = GIT_CVAR_TRUE,
- /* core.abbrev */
- GIT_ABBREV_DEFAULT = 7,
- /* core.precomposeunicode */
- GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
- /* core.safecrlf */
- GIT_SAFE_CRLF_DEFAULT = GIT_CVAR_FALSE,
- /* core.logallrefupdates */
- GIT_LOGALLREFUPDATES_UNSET = 2,
- GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET,
- /* core.protectHFS */
- GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE,
- /* core.protectNTFS */
- GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE,
-} git_cvar_value;
-
-/* internal repository init flags */
-enum {
- GIT_REPOSITORY_INIT__HAS_DOTGIT = (1u << 16),
- GIT_REPOSITORY_INIT__NATURAL_WD = (1u << 17),
- GIT_REPOSITORY_INIT__IS_REINIT = (1u << 18),
-};
-
-/** Internal structure for repository object */
-struct git_repository {
- git_odb *_odb;
- git_refdb *_refdb;
- git_config *_config;
- git_index *_index;
-
- git_cache objects;
- git_attr_cache *attrcache;
- git_diff_driver_registry *diff_drivers;
-
- char *path_repository;
- char *path_gitlink;
- char *workdir;
- char *namespace;
-
- char *ident_name;
- char *ident_email;
-
- git_array_t(git_buf) reserved_names;
-
- unsigned is_bare:1;
-
- unsigned int lru_counter;
-
- git_atomic attr_session_key;
-
- git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
-};
-
-GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
-{
- return repo->attrcache;
-}
-
-int git_repository_head_tree(git_tree **tree, git_repository *repo);
-
-/*
- * Weak pointers to repository internals.
- *
- * The returned pointers do not need to be freed. Do not keep
- * permanent references to these (i.e. between API calls), since they may
- * become invalidated if the user replaces a repository internal.
- */
-int git_repository_config__weakptr(git_config **out, git_repository *repo);
-int git_repository_odb__weakptr(git_odb **out, git_repository *repo);
-int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo);
-int git_repository_index__weakptr(git_index **out, git_repository *repo);
-
-/*
- * CVAR cache
- *
- * Efficient access to the most used config variables of a repository.
- * The cache is cleared every time the config backend is replaced.
- */
-int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar);
-void git_repository__cvar_cache_clear(git_repository *repo);
-
-GIT_INLINE(int) git_repository__ensure_not_bare(
- git_repository *repo,
- const char *operation_name)
-{
- if (!git_repository_is_bare(repo))
- return 0;
-
- giterr_set(
- GITERR_REPOSITORY,
- "Cannot %s. This operation is not allowed against bare repositories.",
- operation_name);
-
- return GIT_EBAREREPO;
-}
-
-int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head);
-
-int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len);
-
-/* The default "reserved names" for a repository */
-extern git_buf git_repository__reserved_names_win32[];
-extern size_t git_repository__reserved_names_win32_len;
-
-extern git_buf git_repository__reserved_names_posix[];
-extern size_t git_repository__reserved_names_posix_len;
-
-/*
- * Gets any "reserved names" in the repository. This will return paths
- * that should not be allowed in the repository (like ".git") to avoid
- * conflicting with the repository path, or with alternate mechanisms to
- * the repository path (eg, "GIT~1"). Every attempt will be made to look
- * up all possible reserved names - if there was a conflict for the shortname
- * GIT~1, for example, this function will try to look up the alternate
- * shortname. If that fails, this function returns false, but out and outlen
- * will still be populated with good defaults.
- */
-bool git_repository__reserved_names(
- git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "commit.h"
-#include "tag.h"
-#include "merge.h"
-#include "diff.h"
-#include "annotated_commit.h"
-#include "git2/reset.h"
-#include "git2/checkout.h"
-#include "git2/merge.h"
-#include "git2/refs.h"
-
-#define ERROR_MSG "Cannot perform reset"
-
-int git_reset_default(
- git_repository *repo,
- git_object *target,
- git_strarray* pathspecs)
-{
- git_object *commit = NULL;
- git_tree *tree = NULL;
- git_diff *diff = NULL;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- size_t i, max_i;
- git_index_entry entry;
- int error;
- git_index *index = NULL;
-
- assert(pathspecs != NULL && pathspecs->count > 0);
-
- memset(&entry, 0, sizeof(git_index_entry));
-
- if ((error = git_repository_index(&index, repo)) < 0)
- goto cleanup;
-
- if (target) {
- if (git_object_owner(target) != repo) {
- giterr_set(GITERR_OBJECT,
- "%s_default - The given target does not belong to this repository.", ERROR_MSG);
- return -1;
- }
-
- if ((error = git_object_peel(&commit, target, GIT_OBJ_COMMIT)) < 0 ||
- (error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
- goto cleanup;
- }
-
- opts.pathspec = *pathspecs;
- opts.flags = GIT_DIFF_REVERSE;
-
- if ((error = git_diff_tree_to_index(
- &diff, repo, tree, index, &opts)) < 0)
- goto cleanup;
-
- for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) {
- const git_diff_delta *delta = git_diff_get_delta(diff, i);
-
- assert(delta->status == GIT_DELTA_ADDED ||
- delta->status == GIT_DELTA_MODIFIED ||
- delta->status == GIT_DELTA_CONFLICTED ||
- delta->status == GIT_DELTA_DELETED);
-
- error = git_index_conflict_remove(index, delta->old_file.path);
- if (error < 0) {
- if (delta->status == GIT_DELTA_ADDED && error == GIT_ENOTFOUND)
- giterr_clear();
- else
- goto cleanup;
- }
-
- if (delta->status == GIT_DELTA_DELETED) {
- if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
- goto cleanup;
- } else {
- entry.mode = delta->new_file.mode;
- git_oid_cpy(&entry.id, &delta->new_file.id);
- entry.path = (char *)delta->new_file.path;
-
- if ((error = git_index_add(index, &entry)) < 0)
- goto cleanup;
- }
- }
-
- error = git_index_write(index);
-
-cleanup:
- git_object_free(commit);
- git_tree_free(tree);
- git_index_free(index);
- git_diff_free(diff);
-
- return error;
-}
-
-static int reset(
- git_repository *repo,
- git_object *target,
- const char *to,
- git_reset_t reset_type,
- const git_checkout_options *checkout_opts)
-{
- git_object *commit = NULL;
- git_index *index = NULL;
- git_tree *tree = NULL;
- int error = 0;
- git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
- git_buf log_message = GIT_BUF_INIT;
-
- assert(repo && target);
-
- if (checkout_opts)
- opts = *checkout_opts;
-
- if (git_object_owner(target) != repo) {
- giterr_set(GITERR_OBJECT,
- "%s - The given target does not belong to this repository.", ERROR_MSG);
- return -1;
- }
-
- if (reset_type != GIT_RESET_SOFT &&
- (error = git_repository__ensure_not_bare(repo,
- reset_type == GIT_RESET_MIXED ? "reset mixed" : "reset hard")) < 0)
- return error;
-
- if ((error = git_object_peel(&commit, target, GIT_OBJ_COMMIT)) < 0 ||
- (error = git_repository_index(&index, repo)) < 0 ||
- (error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
- goto cleanup;
-
- if (reset_type == GIT_RESET_SOFT &&
- (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE ||
- git_index_has_conflicts(index)))
- {
- giterr_set(GITERR_OBJECT, "%s (soft) in the middle of a merge.", ERROR_MSG);
- error = GIT_EUNMERGED;
- goto cleanup;
- }
-
- if ((error = git_buf_printf(&log_message, "reset: moving to %s", to)) < 0)
- return error;
-
- if (reset_type == GIT_RESET_HARD) {
- /* overwrite working directory with the new tree */
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
- goto cleanup;
- }
-
- /* move HEAD to the new target */
- if ((error = git_reference__update_terminal(repo, GIT_HEAD_FILE,
- git_object_id(commit), NULL, git_buf_cstr(&log_message))) < 0)
- goto cleanup;
-
- if (reset_type > GIT_RESET_SOFT) {
- /* reset index to the target content */
-
- if ((error = git_index_read_tree(index, tree)) < 0 ||
- (error = git_index_write(index)) < 0)
- goto cleanup;
-
- if ((error = git_repository_state_cleanup(repo)) < 0) {
- giterr_set(GITERR_INDEX, "%s - failed to clean up merge data", ERROR_MSG);
- goto cleanup;
- }
- }
-
-cleanup:
- git_object_free(commit);
- git_index_free(index);
- git_tree_free(tree);
- git_buf_free(&log_message);
-
- return error;
-}
-
-int git_reset(
- git_repository *repo,
- git_object *target,
- git_reset_t reset_type,
- const git_checkout_options *checkout_opts)
-{
- return reset(repo, target, git_oid_tostr_s(git_object_id(target)), reset_type, checkout_opts);
-}
-
-int git_reset_from_annotated(
- git_repository *repo,
- git_annotated_commit *commit,
- git_reset_t reset_type,
- const git_checkout_options *checkout_opts)
-{
- return reset(repo, (git_object *) commit->commit, commit->description, reset_type, checkout_opts);
-}
+++ /dev/null
-/*
-* Copyright (C) the libgit2 contributors. All rights reserved.
-*
-* This file is part of libgit2, distributed under the GNU GPL v2 with
-* a Linking Exception. For full terms see the included COPYING file.
-*/
-
-#include "common.h"
-#include "repository.h"
-#include "filebuf.h"
-#include "merge.h"
-#include "index.h"
-
-#include "git2/types.h"
-#include "git2/merge.h"
-#include "git2/revert.h"
-#include "git2/commit.h"
-#include "git2/sys/commit.h"
-
-#define GIT_REVERT_FILE_MODE 0666
-
-static int write_revert_head(
- git_repository *repo,
- const char *commit_oidstr)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf file_path = GIT_BUF_INIT;
- int error = 0;
-
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_REVERT_HEAD_FILE)) >= 0 &&
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) >= 0 &&
- (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0)
- error = git_filebuf_commit(&file);
-
- if (error < 0)
- git_filebuf_cleanup(&file);
-
- git_buf_free(&file_path);
-
- return error;
-}
-
-static int write_merge_msg(
- git_repository *repo,
- const char *commit_oidstr,
- const char *commit_msgline)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf file_path = GIT_BUF_INIT;
- int error = 0;
-
- if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) < 0 ||
- (error = git_filebuf_printf(&file, "Revert \"%s\"\n\nThis reverts commit %s.\n",
- commit_msgline, commit_oidstr)) < 0)
- goto cleanup;
-
- error = git_filebuf_commit(&file);
-
-cleanup:
- if (error < 0)
- git_filebuf_cleanup(&file);
-
- git_buf_free(&file_path);
-
- return error;
-}
-
-static int revert_normalize_opts(
- git_repository *repo,
- git_revert_options *opts,
- const git_revert_options *given,
- const char *their_label)
-{
- int error = 0;
- unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE |
- GIT_CHECKOUT_ALLOW_CONFLICTS;
-
- GIT_UNUSED(repo);
-
- if (given != NULL)
- memcpy(opts, given, sizeof(git_revert_options));
- else {
- git_revert_options default_opts = GIT_REVERT_OPTIONS_INIT;
- memcpy(opts, &default_opts, sizeof(git_revert_options));
- }
-
- if (!opts->checkout_opts.checkout_strategy)
- opts->checkout_opts.checkout_strategy = default_checkout_strategy;
-
- if (!opts->checkout_opts.our_label)
- opts->checkout_opts.our_label = "HEAD";
-
- if (!opts->checkout_opts.their_label)
- opts->checkout_opts.their_label = their_label;
-
- return error;
-}
-
-static int revert_state_cleanup(git_repository *repo)
-{
- const char *state_files[] = { GIT_REVERT_HEAD_FILE, GIT_MERGE_MSG_FILE };
-
- return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
-}
-
-static int revert_seterr(git_commit *commit, const char *fmt)
-{
- char commit_oidstr[GIT_OID_HEXSZ + 1];
-
- git_oid_fmt(commit_oidstr, git_commit_id(commit));
- commit_oidstr[GIT_OID_HEXSZ] = '\0';
-
- giterr_set(GITERR_REVERT, fmt, commit_oidstr);
-
- return -1;
-}
-
-int git_revert_commit(
- git_index **out,
- git_repository *repo,
- git_commit *revert_commit,
- git_commit *our_commit,
- unsigned int mainline,
- const git_merge_options *merge_opts)
-{
- git_commit *parent_commit = NULL;
- git_tree *parent_tree = NULL, *our_tree = NULL, *revert_tree = NULL;
- int parent = 0, error = 0;
-
- assert(out && repo && revert_commit && our_commit);
-
- if (git_commit_parentcount(revert_commit) > 1) {
- if (!mainline)
- return revert_seterr(revert_commit,
- "Mainline branch is not specified but %s is a merge commit");
-
- parent = mainline;
- } else {
- if (mainline)
- return revert_seterr(revert_commit,
- "Mainline branch specified but %s is not a merge commit");
-
- parent = git_commit_parentcount(revert_commit);
- }
-
- if (parent &&
- ((error = git_commit_parent(&parent_commit, revert_commit, (parent - 1))) < 0 ||
- (error = git_commit_tree(&parent_tree, parent_commit)) < 0))
- goto done;
-
- if ((error = git_commit_tree(&revert_tree, revert_commit)) < 0 ||
- (error = git_commit_tree(&our_tree, our_commit)) < 0)
- goto done;
-
- error = git_merge_trees(out, repo, revert_tree, our_tree, parent_tree, merge_opts);
-
-done:
- git_tree_free(parent_tree);
- git_tree_free(our_tree);
- git_tree_free(revert_tree);
- git_commit_free(parent_commit);
-
- return error;
-}
-
-int git_revert(
- git_repository *repo,
- git_commit *commit,
- const git_revert_options *given_opts)
-{
- git_revert_options opts;
- git_reference *our_ref = NULL;
- git_commit *our_commit = NULL;
- char commit_oidstr[GIT_OID_HEXSZ + 1];
- const char *commit_msg;
- git_buf their_label = GIT_BUF_INIT;
- git_index *index = NULL;
- git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
- int error;
-
- assert(repo && commit);
-
- GITERR_CHECK_VERSION(given_opts, GIT_REVERT_OPTIONS_VERSION, "git_revert_options");
-
- if ((error = git_repository__ensure_not_bare(repo, "revert")) < 0)
- return error;
-
- git_oid_fmt(commit_oidstr, git_commit_id(commit));
- commit_oidstr[GIT_OID_HEXSZ] = '\0';
-
- if ((commit_msg = git_commit_summary(commit)) == NULL) {
- error = -1;
- goto on_error;
- }
-
- if ((error = git_buf_printf(&their_label, "parent of %.7s... %s", commit_oidstr, commit_msg)) < 0 ||
- (error = revert_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 ||
- (error = git_indexwriter_init_for_operation(&indexwriter, repo, &opts.checkout_opts.checkout_strategy)) < 0 ||
- (error = write_revert_head(repo, commit_oidstr)) < 0 ||
- (error = write_merge_msg(repo, commit_oidstr, commit_msg)) < 0 ||
- (error = git_repository_head(&our_ref, repo)) < 0 ||
- (error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 ||
- (error = git_revert_commit(&index, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
- (error = git_merge__check_result(repo, index)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0 ||
- (error = git_checkout_index(repo, index, &opts.checkout_opts)) < 0 ||
- (error = git_indexwriter_commit(&indexwriter)) < 0)
- goto on_error;
-
- goto done;
-
-on_error:
- revert_state_cleanup(repo);
-
-done:
- git_indexwriter_cleanup(&indexwriter);
- git_index_free(index);
- git_commit_free(our_commit);
- git_reference_free(our_ref);
- git_buf_free(&their_label);
-
- return error;
-}
-
-int git_revert_init_options(git_revert_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_revert_options, GIT_REVERT_OPTIONS_INIT);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include <assert.h>
-
-#include "common.h"
-#include "buffer.h"
-#include "tree.h"
-#include "refdb.h"
-
-#include "git2.h"
-
-static int maybe_sha_or_abbrev(git_object** out, git_repository *repo, const char *spec, size_t speclen)
-{
- git_oid oid;
-
- if (git_oid_fromstrn(&oid, spec, speclen) < 0)
- return GIT_ENOTFOUND;
-
- return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJ_ANY);
-}
-
-static int maybe_sha(git_object** out, git_repository *repo, const char *spec)
-{
- size_t speclen = strlen(spec);
-
- if (speclen != GIT_OID_HEXSZ)
- return GIT_ENOTFOUND;
-
- return maybe_sha_or_abbrev(out, repo, spec, speclen);
-}
-
-static int maybe_abbrev(git_object** out, git_repository *repo, const char *spec)
-{
- size_t speclen = strlen(spec);
-
- return maybe_sha_or_abbrev(out, repo, spec, speclen);
-}
-
-static int build_regex(regex_t *regex, const char *pattern)
-{
- int error;
-
- if (*pattern == '\0') {
- giterr_set(GITERR_REGEX, "Empty pattern");
- return GIT_EINVALIDSPEC;
- }
-
- error = p_regcomp(regex, pattern, REG_EXTENDED);
- if (!error)
- return 0;
-
- error = giterr_set_regex(regex, error);
-
- regfree(regex);
-
- return error;
-}
-
-static int maybe_describe(git_object**out, git_repository *repo, const char *spec)
-{
- const char *substr;
- int error;
- regex_t regex;
-
- substr = strstr(spec, "-g");
-
- if (substr == NULL)
- return GIT_ENOTFOUND;
-
- if (build_regex(®ex, ".+-[0-9]+-g[0-9a-fA-F]+") < 0)
- return -1;
-
- error = regexec(®ex, spec, 0, NULL, 0);
- regfree(®ex);
-
- if (error)
- return GIT_ENOTFOUND;
-
- return maybe_abbrev(out, repo, substr+2);
-}
-
-static int revparse_lookup_object(
- git_object **object_out,
- git_reference **reference_out,
- git_repository *repo,
- const char *spec)
-{
- int error;
- git_reference *ref;
-
- if ((error = maybe_sha(object_out, repo, spec)) != GIT_ENOTFOUND)
- return error;
-
- error = git_reference_dwim(&ref, repo, spec);
- if (!error) {
-
- error = git_object_lookup(
- object_out, repo, git_reference_target(ref), GIT_OBJ_ANY);
-
- if (!error)
- *reference_out = ref;
-
- return error;
- }
-
- if (error != GIT_ENOTFOUND)
- return error;
-
- if ((strlen(spec) < GIT_OID_HEXSZ) &&
- ((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND))
- return error;
-
- if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND)
- return error;
-
- giterr_set(GITERR_REFERENCE, "Revspec '%s' not found.", spec);
- return GIT_ENOTFOUND;
-}
-
-static int try_parse_numeric(int *n, const char *curly_braces_content)
-{
- int32_t content;
- const char *end_ptr;
-
- if (git__strtol32(&content, curly_braces_content, &end_ptr, 10) < 0)
- return -1;
-
- if (*end_ptr != '\0')
- return -1;
-
- *n = (int)content;
- return 0;
-}
-
-static int retrieve_previously_checked_out_branch_or_revision(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position)
-{
- git_reference *ref = NULL;
- git_reflog *reflog = NULL;
- regex_t preg;
- int error = -1;
- size_t i, numentries, cur;
- const git_reflog_entry *entry;
- const char *msg;
- regmatch_t regexmatches[2];
- git_buf buf = GIT_BUF_INIT;
-
- cur = position;
-
- if (*identifier != '\0' || *base_ref != NULL)
- return GIT_EINVALIDSPEC;
-
- if (build_regex(&preg, "checkout: moving from (.*) to .*") < 0)
- return -1;
-
- if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
- goto cleanup;
-
- if (git_reflog_read(&reflog, repo, GIT_HEAD_FILE) < 0)
- goto cleanup;
-
- numentries = git_reflog_entrycount(reflog);
-
- for (i = 0; i < numentries; i++) {
- entry = git_reflog_entry_byindex(reflog, i);
- msg = git_reflog_entry_message(entry);
- if (!msg)
- continue;
-
- if (regexec(&preg, msg, 2, regexmatches, 0))
- continue;
-
- cur--;
-
- if (cur > 0)
- continue;
-
- git_buf_put(&buf, msg+regexmatches[1].rm_so, regexmatches[1].rm_eo - regexmatches[1].rm_so);
-
- if ((error = git_reference_dwim(base_ref, repo, git_buf_cstr(&buf))) == 0)
- goto cleanup;
-
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- error = maybe_abbrev(out, repo, git_buf_cstr(&buf));
-
- goto cleanup;
- }
-
- error = GIT_ENOTFOUND;
-
-cleanup:
- git_reference_free(ref);
- git_buf_free(&buf);
- regfree(&preg);
- git_reflog_free(reflog);
- return error;
-}
-
-static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t identifier)
-{
- git_reflog *reflog;
- size_t numentries;
- const git_reflog_entry *entry;
- bool search_by_pos = (identifier <= 100000000);
-
- if (git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)) < 0)
- return -1;
-
- numentries = git_reflog_entrycount(reflog);
-
- if (search_by_pos) {
- if (numentries < identifier + 1)
- goto notfound;
-
- entry = git_reflog_entry_byindex(reflog, identifier);
- git_oid_cpy(oid, git_reflog_entry_id_new(entry));
- } else {
- size_t i;
- git_time commit_time;
-
- for (i = 0; i < numentries; i++) {
- entry = git_reflog_entry_byindex(reflog, i);
- commit_time = git_reflog_entry_committer(entry)->when;
-
- if (commit_time.time > (git_time_t)identifier)
- continue;
-
- git_oid_cpy(oid, git_reflog_entry_id_new(entry));
- break;
- }
-
- if (i == numentries)
- goto notfound;
- }
-
- git_reflog_free(reflog);
- return 0;
-
-notfound:
- giterr_set(
- GITERR_REFERENCE,
- "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
- git_reference_name(ref), numentries, identifier);
-
- git_reflog_free(reflog);
- return GIT_ENOTFOUND;
-}
-
-static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position)
-{
- git_reference *ref;
- git_oid oid;
- int error = -1;
-
- if (*base_ref == NULL) {
- if ((error = git_reference_dwim(&ref, repo, identifier)) < 0)
- return error;
- } else {
- ref = *base_ref;
- *base_ref = NULL;
- }
-
- if (position == 0) {
- error = git_object_lookup(out, repo, git_reference_target(ref), GIT_OBJ_ANY);
- goto cleanup;
- }
-
- if ((error = retrieve_oid_from_reflog(&oid, ref, position)) < 0)
- goto cleanup;
-
- error = git_object_lookup(out, repo, &oid, GIT_OBJ_ANY);
-
-cleanup:
- git_reference_free(ref);
- return error;
-}
-
-static int retrieve_remote_tracking_reference(git_reference **base_ref, const char *identifier, git_repository *repo)
-{
- git_reference *tracking, *ref;
- int error = -1;
-
- if (*base_ref == NULL) {
- if ((error = git_reference_dwim(&ref, repo, identifier)) < 0)
- return error;
- } else {
- ref = *base_ref;
- *base_ref = NULL;
- }
-
- if (!git_reference_is_branch(ref)) {
- error = GIT_EINVALIDSPEC;
- goto cleanup;
- }
-
- if ((error = git_branch_upstream(&tracking, ref)) < 0)
- goto cleanup;
-
- *base_ref = tracking;
-
-cleanup:
- git_reference_free(ref);
- return error;
-}
-
-static int handle_at_syntax(git_object **out, git_reference **ref, const char *spec, size_t identifier_len, git_repository* repo, const char *curly_braces_content)
-{
- bool is_numeric;
- int parsed = 0, error = -1;
- git_buf identifier = GIT_BUF_INIT;
- git_time_t timestamp;
-
- assert(*out == NULL);
-
- if (git_buf_put(&identifier, spec, identifier_len) < 0)
- return -1;
-
- is_numeric = !try_parse_numeric(&parsed, curly_braces_content);
-
- if (*curly_braces_content == '-' && (!is_numeric || parsed == 0)) {
- error = GIT_EINVALIDSPEC;
- goto cleanup;
- }
-
- if (is_numeric) {
- if (parsed < 0)
- error = retrieve_previously_checked_out_branch_or_revision(out, ref, repo, git_buf_cstr(&identifier), -parsed);
- else
- error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), parsed);
-
- goto cleanup;
- }
-
- if (!strcmp(curly_braces_content, "u") || !strcmp(curly_braces_content, "upstream")) {
- error = retrieve_remote_tracking_reference(ref, git_buf_cstr(&identifier), repo);
-
- goto cleanup;
- }
-
- if (git__date_parse(×tamp, curly_braces_content) < 0)
- goto cleanup;
-
- error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), (size_t)timestamp);
-
-cleanup:
- git_buf_free(&identifier);
- return error;
-}
-
-static git_otype parse_obj_type(const char *str)
-{
- if (!strcmp(str, "commit"))
- return GIT_OBJ_COMMIT;
-
- if (!strcmp(str, "tree"))
- return GIT_OBJ_TREE;
-
- if (!strcmp(str, "blob"))
- return GIT_OBJ_BLOB;
-
- if (!strcmp(str, "tag"))
- return GIT_OBJ_TAG;
-
- return GIT_OBJ_BAD;
-}
-
-static int dereference_to_non_tag(git_object **out, git_object *obj)
-{
- if (git_object_type(obj) == GIT_OBJ_TAG)
- return git_tag_peel(out, (git_tag *)obj);
-
- return git_object_dup(out, obj);
-}
-
-static int handle_caret_parent_syntax(git_object **out, git_object *obj, int n)
-{
- git_object *temp_commit = NULL;
- int error;
-
- if ((error = git_object_peel(&temp_commit, obj, GIT_OBJ_COMMIT)) < 0)
- return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ?
- GIT_EINVALIDSPEC : error;
-
- if (n == 0) {
- *out = temp_commit;
- return 0;
- }
-
- error = git_commit_parent((git_commit **)out, (git_commit*)temp_commit, n - 1);
-
- git_object_free(temp_commit);
- return error;
-}
-
-static int handle_linear_syntax(git_object **out, git_object *obj, int n)
-{
- git_object *temp_commit = NULL;
- int error;
-
- if ((error = git_object_peel(&temp_commit, obj, GIT_OBJ_COMMIT)) < 0)
- return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ?
- GIT_EINVALIDSPEC : error;
-
- error = git_commit_nth_gen_ancestor((git_commit **)out, (git_commit*)temp_commit, n);
-
- git_object_free(temp_commit);
- return error;
-}
-
-static int handle_colon_syntax(
- git_object **out,
- git_object *obj,
- const char *path)
-{
- git_object *tree;
- int error = -1;
- git_tree_entry *entry = NULL;
-
- if ((error = git_object_peel(&tree, obj, GIT_OBJ_TREE)) < 0)
- return error == GIT_ENOTFOUND ? GIT_EINVALIDSPEC : error;
-
- if (*path == '\0') {
- *out = tree;
- return 0;
- }
-
- /*
- * TODO: Handle the relative path syntax
- * (:./relative/path and :../relative/path)
- */
- if ((error = git_tree_entry_bypath(&entry, (git_tree *)tree, path)) < 0)
- goto cleanup;
-
- error = git_tree_entry_to_object(out, git_object_owner(tree), entry);
-
-cleanup:
- git_tree_entry_free(entry);
- git_object_free(tree);
-
- return error;
-}
-
-static int walk_and_search(git_object **out, git_revwalk *walk, regex_t *regex)
-{
- int error;
- git_oid oid;
- git_object *obj;
-
- while (!(error = git_revwalk_next(&oid, walk))) {
-
- error = git_object_lookup(&obj, git_revwalk_repository(walk), &oid, GIT_OBJ_COMMIT);
- if ((error < 0) && (error != GIT_ENOTFOUND))
- return -1;
-
- if (!regexec(regex, git_commit_message((git_commit*)obj), 0, NULL, 0)) {
- *out = obj;
- return 0;
- }
-
- git_object_free(obj);
- }
-
- if (error < 0 && error == GIT_ITEROVER)
- error = GIT_ENOTFOUND;
-
- return error;
-}
-
-static int handle_grep_syntax(git_object **out, git_repository *repo, const git_oid *spec_oid, const char *pattern)
-{
- regex_t preg;
- git_revwalk *walk = NULL;
- int error;
-
- if ((error = build_regex(&preg, pattern)) < 0)
- return error;
-
- if ((error = git_revwalk_new(&walk, repo)) < 0)
- goto cleanup;
-
- git_revwalk_sorting(walk, GIT_SORT_TIME);
-
- if (spec_oid == NULL) {
- if ((error = git_revwalk_push_glob(walk, "refs/*")) < 0)
- goto cleanup;
- } else if ((error = git_revwalk_push(walk, spec_oid)) < 0)
- goto cleanup;
-
- error = walk_and_search(out, walk, &preg);
-
-cleanup:
- regfree(&preg);
- git_revwalk_free(walk);
-
- return error;
-}
-
-static int handle_caret_curly_syntax(git_object **out, git_object *obj, const char *curly_braces_content)
-{
- git_otype expected_type;
-
- if (*curly_braces_content == '\0')
- return dereference_to_non_tag(out, obj);
-
- if (*curly_braces_content == '/')
- return handle_grep_syntax(out, git_object_owner(obj), git_object_id(obj), curly_braces_content + 1);
-
- expected_type = parse_obj_type(curly_braces_content);
-
- if (expected_type == GIT_OBJ_BAD)
- return GIT_EINVALIDSPEC;
-
- return git_object_peel(out, obj, expected_type);
-}
-
-static int extract_curly_braces_content(git_buf *buf, const char *spec, size_t *pos)
-{
- git_buf_clear(buf);
-
- assert(spec[*pos] == '^' || spec[*pos] == '@');
-
- (*pos)++;
-
- if (spec[*pos] == '\0' || spec[*pos] != '{')
- return GIT_EINVALIDSPEC;
-
- (*pos)++;
-
- while (spec[*pos] != '}') {
- if (spec[*pos] == '\0')
- return GIT_EINVALIDSPEC;
-
- git_buf_putc(buf, spec[(*pos)++]);
- }
-
- (*pos)++;
-
- return 0;
-}
-
-static int extract_path(git_buf *buf, const char *spec, size_t *pos)
-{
- git_buf_clear(buf);
-
- assert(spec[*pos] == ':');
-
- (*pos)++;
-
- if (git_buf_puts(buf, spec + *pos) < 0)
- return -1;
-
- *pos += git_buf_len(buf);
-
- return 0;
-}
-
-static int extract_how_many(int *n, const char *spec, size_t *pos)
-{
- const char *end_ptr;
- int parsed, accumulated;
- char kind = spec[*pos];
-
- assert(spec[*pos] == '^' || spec[*pos] == '~');
-
- accumulated = 0;
-
- do {
- do {
- (*pos)++;
- accumulated++;
- } while (spec[(*pos)] == kind && kind == '~');
-
- if (git__isdigit(spec[*pos])) {
- if (git__strtol32(&parsed, spec + *pos, &end_ptr, 10) < 0)
- return GIT_EINVALIDSPEC;
-
- accumulated += (parsed - 1);
- *pos = end_ptr - spec;
- }
-
- } while (spec[(*pos)] == kind && kind == '~');
-
- *n = accumulated;
-
- return 0;
-}
-
-static int object_from_reference(git_object **object, git_reference *reference)
-{
- git_reference *resolved = NULL;
- int error;
-
- if (git_reference_resolve(&resolved, reference) < 0)
- return -1;
-
- error = git_object_lookup(object, reference->db->repo, git_reference_target(resolved), GIT_OBJ_ANY);
- git_reference_free(resolved);
-
- return error;
-}
-
-static int ensure_base_rev_loaded(git_object **object, git_reference **reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier)
-{
- int error;
- git_buf identifier = GIT_BUF_INIT;
-
- if (*object != NULL)
- return 0;
-
- if (*reference != NULL)
- return object_from_reference(object, *reference);
-
- if (!allow_empty_identifier && identifier_len == 0)
- return GIT_EINVALIDSPEC;
-
- if (git_buf_put(&identifier, spec, identifier_len) < 0)
- return -1;
-
- error = revparse_lookup_object(object, reference, repo, git_buf_cstr(&identifier));
- git_buf_free(&identifier);
-
- return error;
-}
-
-static int ensure_base_rev_is_not_known_yet(git_object *object)
-{
- if (object == NULL)
- return 0;
-
- return GIT_EINVALIDSPEC;
-}
-
-static bool any_left_hand_identifier(git_object *object, git_reference *reference, size_t identifier_len)
-{
- if (object != NULL)
- return true;
-
- if (reference != NULL)
- return true;
-
- if (identifier_len > 0)
- return true;
-
- return false;
-}
-
-static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference)
-{
- if (!ensure_base_rev_is_not_known_yet(object) && reference == NULL)
- return 0;
-
- return GIT_EINVALIDSPEC;
-}
-
-int revparse__ext(
- git_object **object_out,
- git_reference **reference_out,
- size_t *identifier_len_out,
- git_repository *repo,
- const char *spec)
-{
- size_t pos = 0, identifier_len = 0;
- int error = -1, n;
- git_buf buf = GIT_BUF_INIT;
-
- git_reference *reference = NULL;
- git_object *base_rev = NULL;
-
- bool should_return_reference = true;
-
- assert(object_out && reference_out && repo && spec);
-
- *object_out = NULL;
- *reference_out = NULL;
-
- while (spec[pos]) {
- switch (spec[pos]) {
- case '^':
- should_return_reference = false;
-
- if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
- goto cleanup;
-
- if (spec[pos+1] == '{') {
- git_object *temp_object = NULL;
-
- if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0)
- goto cleanup;
-
- if ((error = handle_caret_curly_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0)
- goto cleanup;
-
- git_object_free(base_rev);
- base_rev = temp_object;
- } else {
- git_object *temp_object = NULL;
-
- if ((error = extract_how_many(&n, spec, &pos)) < 0)
- goto cleanup;
-
- if ((error = handle_caret_parent_syntax(&temp_object, base_rev, n)) < 0)
- goto cleanup;
-
- git_object_free(base_rev);
- base_rev = temp_object;
- }
- break;
-
- case '~':
- {
- git_object *temp_object = NULL;
-
- should_return_reference = false;
-
- if ((error = extract_how_many(&n, spec, &pos)) < 0)
- goto cleanup;
-
- if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
- goto cleanup;
-
- if ((error = handle_linear_syntax(&temp_object, base_rev, n)) < 0)
- goto cleanup;
-
- git_object_free(base_rev);
- base_rev = temp_object;
- break;
- }
-
- case ':':
- {
- git_object *temp_object = NULL;
-
- should_return_reference = false;
-
- if ((error = extract_path(&buf, spec, &pos)) < 0)
- goto cleanup;
-
- if (any_left_hand_identifier(base_rev, reference, identifier_len)) {
- if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, true)) < 0)
- goto cleanup;
-
- if ((error = handle_colon_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0)
- goto cleanup;
- } else {
- if (*git_buf_cstr(&buf) == '/') {
- if ((error = handle_grep_syntax(&temp_object, repo, NULL, git_buf_cstr(&buf) + 1)) < 0)
- goto cleanup;
- } else {
-
- /*
- * TODO: support merge-stage path lookup (":2:Makefile")
- * and plain index blob lookup (:i-am/a/blob)
- */
- giterr_set(GITERR_INVALID, "Unimplemented");
- error = GIT_ERROR;
- goto cleanup;
- }
- }
-
- git_object_free(base_rev);
- base_rev = temp_object;
- break;
- }
-
- case '@':
- {
- if (spec[pos+1] == '{') {
- git_object *temp_object = NULL;
-
- if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0)
- goto cleanup;
-
- if ((error = ensure_base_rev_is_not_known_yet(base_rev)) < 0)
- goto cleanup;
-
- if ((error = handle_at_syntax(&temp_object, &reference, spec, identifier_len, repo, git_buf_cstr(&buf))) < 0)
- goto cleanup;
-
- if (temp_object != NULL)
- base_rev = temp_object;
- break;
- } else {
- /* Fall through */
- }
- }
-
- default:
- if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference)) < 0)
- goto cleanup;
-
- pos++;
- identifier_len++;
- }
- }
-
- if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
- goto cleanup;
-
- if (!should_return_reference) {
- git_reference_free(reference);
- reference = NULL;
- }
-
- *object_out = base_rev;
- *reference_out = reference;
- *identifier_len_out = identifier_len;
- error = 0;
-
-cleanup:
- if (error) {
- if (error == GIT_EINVALIDSPEC)
- giterr_set(GITERR_INVALID,
- "Failed to parse revision specifier - Invalid pattern '%s'", spec);
-
- git_object_free(base_rev);
- git_reference_free(reference);
- }
-
- git_buf_free(&buf);
- return error;
-}
-
-int git_revparse_ext(
- git_object **object_out,
- git_reference **reference_out,
- git_repository *repo,
- const char *spec)
-{
- int error;
- size_t identifier_len;
- git_object *obj = NULL;
- git_reference *ref = NULL;
-
- if ((error = revparse__ext(&obj, &ref, &identifier_len, repo, spec)) < 0)
- goto cleanup;
-
- *object_out = obj;
- *reference_out = ref;
- GIT_UNUSED(identifier_len);
-
- return 0;
-
-cleanup:
- git_object_free(obj);
- git_reference_free(ref);
- return error;
-}
-
-int git_revparse_single(git_object **out, git_repository *repo, const char *spec)
-{
- int error;
- git_object *obj = NULL;
- git_reference *ref = NULL;
-
- *out = NULL;
-
- if ((error = git_revparse_ext(&obj, &ref, repo, spec)) < 0)
- goto cleanup;
-
- git_reference_free(ref);
-
- *out = obj;
-
- return 0;
-
-cleanup:
- git_object_free(obj);
- git_reference_free(ref);
- return error;
-}
-
-int git_revparse(
- git_revspec *revspec,
- git_repository *repo,
- const char *spec)
-{
- const char *dotdot;
- int error = 0;
-
- assert(revspec && repo && spec);
-
- memset(revspec, 0x0, sizeof(*revspec));
-
- if ((dotdot = strstr(spec, "..")) != NULL) {
- char *lstr;
- const char *rstr;
- revspec->flags = GIT_REVPARSE_RANGE;
-
- lstr = git__substrdup(spec, dotdot - spec);
- rstr = dotdot + 2;
- if (dotdot[2] == '.') {
- revspec->flags |= GIT_REVPARSE_MERGE_BASE;
- rstr++;
- }
-
- error = git_revparse_single(&revspec->from, repo, lstr);
- if (!error)
- error = git_revparse_single(&revspec->to, repo, rstr);
-
- git__free((void*)lstr);
- } else {
- revspec->flags = GIT_REVPARSE_SINGLE;
- error = git_revparse_single(&revspec->from, repo, spec);
- }
-
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "commit.h"
-#include "odb.h"
-#include "pool.h"
-
-#include "revwalk.h"
-#include "git2/revparse.h"
-#include "merge.h"
-#include "vector.h"
-
-GIT__USE_OIDMAP
-
-git_commit_list_node *git_revwalk__commit_lookup(
- git_revwalk *walk, const git_oid *oid)
-{
- git_commit_list_node *commit;
- khiter_t pos;
- int ret;
-
- /* lookup and reserve space if not already present */
- pos = kh_get(oid, walk->commits, oid);
- if (pos != kh_end(walk->commits))
- return kh_value(walk->commits, pos);
-
- commit = git_commit_list_alloc_node(walk);
- if (commit == NULL)
- return NULL;
-
- git_oid_cpy(&commit->oid, oid);
-
- pos = kh_put(oid, walk->commits, &commit->oid, &ret);
- assert(ret != 0);
- kh_value(walk->commits, pos) = commit;
-
- return commit;
-}
-
-static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting, int from_glob)
-{
- git_oid commit_id;
- int error;
- git_object *obj, *oobj;
- git_commit_list_node *commit;
- git_commit_list *list;
-
- if ((error = git_object_lookup(&oobj, walk->repo, oid, GIT_OBJ_ANY)) < 0)
- return error;
-
- error = git_object_peel(&obj, oobj, GIT_OBJ_COMMIT);
- git_object_free(oobj);
-
- if (error == GIT_ENOTFOUND || error == GIT_EINVALIDSPEC || error == GIT_EPEEL) {
- /* If this comes from e.g. push_glob("tags"), ignore this */
- if (from_glob)
- return 0;
-
- giterr_set(GITERR_INVALID, "Object is not a committish");
- return -1;
- }
- if (error < 0)
- return error;
-
- git_oid_cpy(&commit_id, git_object_id(obj));
- git_object_free(obj);
-
- commit = git_revwalk__commit_lookup(walk, &commit_id);
- if (commit == NULL)
- return -1; /* error already reported by failed lookup */
-
- /* A previous hide already told us we don't want this commit */
- if (commit->uninteresting)
- return 0;
-
- if (uninteresting)
- walk->did_hide = 1;
- else
- walk->did_push = 1;
-
- commit->uninteresting = uninteresting;
- list = walk->user_input;
- if (git_commit_list_insert(commit, &list) == NULL) {
- giterr_set_oom();
- return -1;
- }
-
- walk->user_input = list;
-
- return 0;
-}
-
-int git_revwalk_push(git_revwalk *walk, const git_oid *oid)
-{
- assert(walk && oid);
- return push_commit(walk, oid, 0, false);
-}
-
-
-int git_revwalk_hide(git_revwalk *walk, const git_oid *oid)
-{
- assert(walk && oid);
- return push_commit(walk, oid, 1, false);
-}
-
-static int push_ref(git_revwalk *walk, const char *refname, int hide, int from_glob)
-{
- git_oid oid;
-
- if (git_reference_name_to_id(&oid, walk->repo, refname) < 0)
- return -1;
-
- return push_commit(walk, &oid, hide, from_glob);
-}
-
-static int push_glob(git_revwalk *walk, const char *glob, int hide)
-{
- int error = 0;
- git_buf buf = GIT_BUF_INIT;
- git_reference *ref;
- git_reference_iterator *iter;
- size_t wildcard;
-
- assert(walk && glob);
-
- /* refs/ is implied if not given in the glob */
- if (git__prefixcmp(glob, GIT_REFS_DIR) != 0)
- git_buf_joinpath(&buf, GIT_REFS_DIR, glob);
- else
- git_buf_puts(&buf, glob);
- GITERR_CHECK_ALLOC_BUF(&buf);
-
- /* If no '?', '*' or '[' exist, we append '/ *' to the glob */
- wildcard = strcspn(glob, "?*[");
- if (!glob[wildcard])
- git_buf_put(&buf, "/*", 2);
-
- if ((error = git_reference_iterator_glob_new(&iter, walk->repo, buf.ptr)) < 0)
- goto out;
-
- while ((error = git_reference_next(&ref, iter)) == 0) {
- error = push_ref(walk, git_reference_name(ref), hide, true);
- git_reference_free(ref);
- if (error < 0)
- break;
- }
- git_reference_iterator_free(iter);
-
- if (error == GIT_ITEROVER)
- error = 0;
-out:
- git_buf_free(&buf);
- return error;
-}
-
-int git_revwalk_push_glob(git_revwalk *walk, const char *glob)
-{
- assert(walk && glob);
- return push_glob(walk, glob, 0);
-}
-
-int git_revwalk_hide_glob(git_revwalk *walk, const char *glob)
-{
- assert(walk && glob);
- return push_glob(walk, glob, 1);
-}
-
-int git_revwalk_push_head(git_revwalk *walk)
-{
- assert(walk);
- return push_ref(walk, GIT_HEAD_FILE, 0, false);
-}
-
-int git_revwalk_hide_head(git_revwalk *walk)
-{
- assert(walk);
- return push_ref(walk, GIT_HEAD_FILE, 1, false);
-}
-
-int git_revwalk_push_ref(git_revwalk *walk, const char *refname)
-{
- assert(walk && refname);
- return push_ref(walk, refname, 0, false);
-}
-
-int git_revwalk_push_range(git_revwalk *walk, const char *range)
-{
- git_revspec revspec;
- int error = 0;
-
- if ((error = git_revparse(&revspec, walk->repo, range)))
- return error;
-
- if (revspec.flags & GIT_REVPARSE_MERGE_BASE) {
- /* TODO: support "<commit>...<commit>" */
- giterr_set(GITERR_INVALID, "Symmetric differences not implemented in revwalk");
- return GIT_EINVALIDSPEC;
- }
-
- if ((error = push_commit(walk, git_object_id(revspec.from), 1, false)))
- goto out;
-
- error = push_commit(walk, git_object_id(revspec.to), 0, false);
-
-out:
- git_object_free(revspec.from);
- git_object_free(revspec.to);
- return error;
-}
-
-int git_revwalk_hide_ref(git_revwalk *walk, const char *refname)
-{
- assert(walk && refname);
- return push_ref(walk, refname, 1, false);
-}
-
-static int revwalk_enqueue_timesort(git_revwalk *walk, git_commit_list_node *commit)
-{
- return git_pqueue_insert(&walk->iterator_time, commit);
-}
-
-static int revwalk_enqueue_unsorted(git_revwalk *walk, git_commit_list_node *commit)
-{
- return git_commit_list_insert(commit, &walk->iterator_rand) ? 0 : -1;
-}
-
-static int revwalk_next_timesort(git_commit_list_node **object_out, git_revwalk *walk)
-{
- git_commit_list_node *next;
-
- if ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) {
- *object_out = next;
- return 0;
- }
-
- giterr_clear();
- return GIT_ITEROVER;
-}
-
-static int revwalk_next_unsorted(git_commit_list_node **object_out, git_revwalk *walk)
-{
- git_commit_list_node *next;
-
- while ((next = git_commit_list_pop(&walk->iterator_rand)) != NULL) {
- /* Some commits might become uninteresting after being added to the list */
- if (!next->uninteresting) {
- *object_out = next;
- return 0;
- }
- }
-
- giterr_clear();
- return GIT_ITEROVER;
-}
-
-static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk *walk)
-{
- git_commit_list_node *next;
-
- while ((next = git_commit_list_pop(&walk->iterator_topo)) != NULL) {
- /* Some commits might become uninteresting after being added to the list */
- if (!next->uninteresting) {
- *object_out = next;
- return 0;
- }
- }
-
- giterr_clear();
- return GIT_ITEROVER;
-}
-
-static int revwalk_next_reverse(git_commit_list_node **object_out, git_revwalk *walk)
-{
- *object_out = git_commit_list_pop(&walk->iterator_reverse);
- return *object_out ? 0 : GIT_ITEROVER;
-}
-
-static void mark_parents_uninteresting(git_commit_list_node *commit)
-{
- unsigned short i;
- git_commit_list *parents = NULL;
-
- for (i = 0; i < commit->out_degree; i++)
- git_commit_list_insert(commit->parents[i], &parents);
-
-
- while (parents) {
- commit = git_commit_list_pop(&parents);
-
- while (commit) {
- if (commit->uninteresting)
- break;
-
- commit->uninteresting = 1;
- /*
- * If we've reached this commit some other way
- * already, we need to mark its parents uninteresting
- * as well.
- */
- if (!commit->parents)
- break;
-
- for (i = 0; i < commit->out_degree; i++)
- git_commit_list_insert(commit->parents[i], &parents);
- commit = commit->parents[0];
- }
- }
-}
-
-static int add_parents_to_list(git_revwalk *walk, git_commit_list_node *commit, git_commit_list **list)
-{
- unsigned short i;
- int error;
-
- if (commit->added)
- return 0;
-
- commit->added = 1;
-
- /*
- * Go full on in the uninteresting case as we want to include
- * as many of these as we can.
- *
- * Usually we haven't parsed the parent of a parent, but if we
- * have it we reached it via other means so we want to mark
- * its parents recursively too.
- */
- if (commit->uninteresting) {
- for (i = 0; i < commit->out_degree; i++) {
- git_commit_list_node *p = commit->parents[i];
- p->uninteresting = 1;
-
- /* git does it gently here, but we don't like missing objects */
- if ((error = git_commit_list_parse(walk, p)) < 0)
- return error;
-
- if (p->parents)
- mark_parents_uninteresting(p);
-
- p->seen = 1;
- git_commit_list_insert_by_date(p, list);
- }
-
- return 0;
- }
-
- /*
- * Now on to what we do if the commit is indeed
- * interesting. Here we do want things like first-parent take
- * effect as this is what we'll be showing.
- */
- for (i = 0; i < commit->out_degree; i++) {
- git_commit_list_node *p = commit->parents[i];
-
- if ((error = git_commit_list_parse(walk, p)) < 0)
- return error;
-
- if (walk->hide_cb && walk->hide_cb(&p->oid, walk->hide_cb_payload))
- continue;
-
- if (!p->seen) {
- p->seen = 1;
- git_commit_list_insert_by_date(p, list);
- }
-
- if (walk->first_parent)
- break;
- }
- return 0;
-}
-
-static int everybody_uninteresting(git_commit_list *orig)
-{
- git_commit_list *list = orig;
-
- while (list) {
- git_commit_list_node *commit = list->item;
- list = list->next;
- if (!commit->uninteresting)
- return 0;
- }
-
- return 1;
-}
-
-/* How many unintersting commits we want to look at after we run out of interesting ones */
-#define SLOP 5
-
-static int still_interesting(git_commit_list *list, int64_t time, int slop)
-{
- /* The empty list is pretty boring */
- if (!list)
- return 0;
-
- /*
- * If the destination list has commits with an earlier date
- * than our source we want to continue looking.
- */
- if (time <= list->item->time)
- return SLOP;
-
- /* If we find interesting commits, we reset the slop count */
- if (!everybody_uninteresting(list))
- return SLOP;
-
- /* Everything's uninteresting, reduce the count */
- return slop - 1;
-}
-
-static int limit_list(git_commit_list **out, git_revwalk *walk, git_commit_list *commits)
-{
- int error, slop = SLOP;
- int64_t time = ~0ll;
- git_commit_list *list = commits;
- git_commit_list *newlist = NULL;
- git_commit_list **p = &newlist;
-
- while (list) {
- git_commit_list_node *commit = git_commit_list_pop(&list);
-
- if ((error = add_parents_to_list(walk, commit, &list)) < 0)
- return error;
-
- if (commit->uninteresting) {
- mark_parents_uninteresting(commit);
-
- slop = still_interesting(list, time, slop);
- if (slop)
- continue;
-
- break;
- }
-
- if (!commit->uninteresting && walk->hide_cb && walk->hide_cb(&commit->oid, walk->hide_cb_payload))
- continue;
-
- time = commit->time;
- p = &git_commit_list_insert(commit, p)->next;
- }
-
- git_commit_list_free(&list);
- *out = newlist;
- return 0;
-}
-
-static int sort_in_topological_order(git_commit_list **out, git_revwalk *walk, git_commit_list *list)
-{
- git_commit_list *ll = NULL, *newlist, **pptr;
- git_commit_list_node *next;
- git_pqueue queue;
- git_vector_cmp queue_cmp = NULL;
- unsigned short i;
- int error;
-
- if (walk->sorting & GIT_SORT_TIME)
- queue_cmp = git_commit_list_time_cmp;
-
- if ((error = git_pqueue_init(&queue, 0, 8, queue_cmp)))
- return error;
-
- /*
- * Start by resetting the in-degree to 1 for the commits in
- * our list. We want to go through this list again, so we
- * store it in the commit list as we extract it from the lower
- * machinery.
- */
- for (ll = list; ll; ll = ll->next) {
- ll->item->in_degree = 1;
- }
-
- /*
- * Count up how many children each commit has. We limit
- * ourselves to those commits in the original list (in-degree
- * of 1) avoiding setting it for any parent that was hidden.
- */
- for(ll = list; ll; ll = ll->next) {
- for (i = 0; i < ll->item->out_degree; ++i) {
- git_commit_list_node *parent = ll->item->parents[i];
- if (parent->in_degree)
- parent->in_degree++;
- }
- }
-
- /*
- * Now we find the tips i.e. those not reachable from any other node
- * i.e. those which still have an in-degree of 1.
- */
- for(ll = list; ll; ll = ll->next) {
- if (ll->item->in_degree == 1) {
- if ((error = git_pqueue_insert(&queue, ll->item)))
- goto cleanup;
- }
- }
-
- /*
- * We need to output the tips in the order that they came out of the
- * traversal, so if we're not doing time-sorting, we need to reverse the
- * pqueue in order to get them to come out as we inserted them.
- */
- if ((walk->sorting & GIT_SORT_TIME) == 0)
- git_pqueue_reverse(&queue);
-
-
- pptr = &newlist;
- newlist = NULL;
- while ((next = git_pqueue_pop(&queue)) != NULL) {
- for (i = 0; i < next->out_degree; ++i) {
- git_commit_list_node *parent = next->parents[i];
- if (parent->in_degree == 0)
- continue;
-
- if (--parent->in_degree == 1) {
- if ((error = git_pqueue_insert(&queue, parent)))
- goto cleanup;
- }
- }
-
- /* All the children of 'item' have been emitted (since we got to it via the priority queue) */
- next->in_degree = 0;
-
- pptr = &git_commit_list_insert(next, pptr)->next;
- }
-
- *out = newlist;
- error = 0;
-
-cleanup:
- git_pqueue_free(&queue);
- return error;
-}
-
-static int prepare_walk(git_revwalk *walk)
-{
- int error;
- git_commit_list *list, *commits = NULL;
- git_commit_list_node *next;
-
- /* If there were no pushes, we know that the walk is already over */
- if (!walk->did_push) {
- giterr_clear();
- return GIT_ITEROVER;
- }
-
- for (list = walk->user_input; list; list = list->next) {
- git_commit_list_node *commit = list->item;
- if ((error = git_commit_list_parse(walk, commit)) < 0)
- return error;
-
- if (commit->uninteresting)
- mark_parents_uninteresting(commit);
-
- if (!commit->seen) {
- commit->seen = 1;
- git_commit_list_insert(commit, &commits);
- }
- }
-
- if ((error = limit_list(&commits, walk, commits)) < 0)
- return error;
-
- if (walk->sorting & GIT_SORT_TOPOLOGICAL) {
- error = sort_in_topological_order(&walk->iterator_topo, walk, commits);
- git_commit_list_free(&commits);
-
- if (error < 0)
- return error;
-
- walk->get_next = &revwalk_next_toposort;
- } else if (walk->sorting & GIT_SORT_TIME) {
- for (list = commits; list && !error; list = list->next)
- error = walk->enqueue(walk, list->item);
-
- git_commit_list_free(&commits);
-
- if (error < 0)
- return error;
- } else {
- walk->iterator_rand = commits;
- walk->get_next = revwalk_next_unsorted;
- }
-
- if (walk->sorting & GIT_SORT_REVERSE) {
-
- while ((error = walk->get_next(&next, walk)) == 0)
- if (git_commit_list_insert(next, &walk->iterator_reverse) == NULL)
- return -1;
-
- if (error != GIT_ITEROVER)
- return error;
-
- walk->get_next = &revwalk_next_reverse;
- }
-
- walk->walking = 1;
- return 0;
-}
-
-
-int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
-{
- git_revwalk *walk = git__calloc(1, sizeof(git_revwalk));
- GITERR_CHECK_ALLOC(walk);
-
- walk->commits = git_oidmap_alloc();
- GITERR_CHECK_ALLOC(walk->commits);
-
- if (git_pqueue_init(&walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0)
- return -1;
-
- git_pool_init(&walk->commit_pool, COMMIT_ALLOC);
- walk->get_next = &revwalk_next_unsorted;
- walk->enqueue = &revwalk_enqueue_unsorted;
-
- walk->repo = repo;
-
- if (git_repository_odb(&walk->odb, repo) < 0) {
- git_revwalk_free(walk);
- return -1;
- }
-
- *revwalk_out = walk;
- return 0;
-}
-
-void git_revwalk_free(git_revwalk *walk)
-{
- if (walk == NULL)
- return;
-
- git_revwalk_reset(walk);
- git_odb_free(walk->odb);
-
- git_oidmap_free(walk->commits);
- git_pool_clear(&walk->commit_pool);
- git_pqueue_free(&walk->iterator_time);
- git__free(walk);
-}
-
-git_repository *git_revwalk_repository(git_revwalk *walk)
-{
- assert(walk);
- return walk->repo;
-}
-
-void git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode)
-{
- assert(walk);
-
- if (walk->walking)
- git_revwalk_reset(walk);
-
- walk->sorting = sort_mode;
-
- if (walk->sorting & GIT_SORT_TIME) {
- walk->get_next = &revwalk_next_timesort;
- walk->enqueue = &revwalk_enqueue_timesort;
- } else {
- walk->get_next = &revwalk_next_unsorted;
- walk->enqueue = &revwalk_enqueue_unsorted;
- }
-}
-
-void git_revwalk_simplify_first_parent(git_revwalk *walk)
-{
- walk->first_parent = 1;
-}
-
-int git_revwalk_next(git_oid *oid, git_revwalk *walk)
-{
- int error;
- git_commit_list_node *next;
-
- assert(walk && oid);
-
- if (!walk->walking) {
- if ((error = prepare_walk(walk)) < 0)
- return error;
- }
-
- error = walk->get_next(&next, walk);
-
- if (error == GIT_ITEROVER) {
- git_revwalk_reset(walk);
- giterr_clear();
- return GIT_ITEROVER;
- }
-
- if (!error)
- git_oid_cpy(oid, &next->oid);
-
- return error;
-}
-
-void git_revwalk_reset(git_revwalk *walk)
-{
- git_commit_list_node *commit;
-
- assert(walk);
-
- kh_foreach_value(walk->commits, commit, {
- commit->seen = 0;
- commit->in_degree = 0;
- commit->topo_delay = 0;
- commit->uninteresting = 0;
- commit->added = 0;
- commit->flags = 0;
- });
-
- git_pqueue_clear(&walk->iterator_time);
- git_commit_list_free(&walk->iterator_topo);
- git_commit_list_free(&walk->iterator_rand);
- git_commit_list_free(&walk->iterator_reverse);
- git_commit_list_free(&walk->user_input);
- walk->first_parent = 0;
- walk->walking = 0;
- walk->did_push = walk->did_hide = 0;
-}
-
-int git_revwalk_add_hide_cb(
- git_revwalk *walk,
- git_revwalk_hide_cb hide_cb,
- void *payload)
-{
- assert(walk);
-
- if (walk->walking)
- git_revwalk_reset(walk);
-
- if (walk->hide_cb) {
- /* There is already a callback added */
- giterr_set(GITERR_INVALID, "There is already a callback added to hide commits in revision walker.");
- return -1;
- }
-
- walk->hide_cb = hide_cb;
- walk->hide_cb_payload = payload;
-
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_revwalk_h__
-#define INCLUDE_revwalk_h__
-
-#include "git2/revwalk.h"
-#include "oidmap.h"
-#include "commit_list.h"
-#include "pqueue.h"
-#include "pool.h"
-#include "vector.h"
-
-#include "oidmap.h"
-
-struct git_revwalk {
- git_repository *repo;
- git_odb *odb;
-
- git_oidmap *commits;
- git_pool commit_pool;
-
- git_commit_list *iterator_topo;
- git_commit_list *iterator_rand;
- git_commit_list *iterator_reverse;
- git_pqueue iterator_time;
-
- int (*get_next)(git_commit_list_node **, git_revwalk *);
- int (*enqueue)(git_revwalk *, git_commit_list_node *);
-
- unsigned walking:1,
- first_parent: 1,
- did_hide: 1,
- did_push: 1;
- unsigned int sorting;
-
- /* the pushes and hides */
- git_commit_list *user_input;
-
- /* hide callback */
- git_revwalk_hide_cb hide_cb;
- void *hide_cb_payload;
-};
-
-git_commit_list_node *git_revwalk__commit_lookup(git_revwalk *walk, const git_oid *oid);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifdef GIT_OPENSSL
-# include <openssl/err.h>
-#endif
-
-#include <git2.h>
-#include "common.h"
-#include "sysdir.h"
-#include "cache.h"
-#include "global.h"
-#include "object.h"
-
-void git_libgit2_version(int *major, int *minor, int *rev)
-{
- *major = LIBGIT2_VER_MAJOR;
- *minor = LIBGIT2_VER_MINOR;
- *rev = LIBGIT2_VER_REVISION;
-}
-
-int git_libgit2_features(void)
-{
- return 0
-#ifdef GIT_THREADS
- | GIT_FEATURE_THREADS
-#endif
-#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
- | GIT_FEATURE_HTTPS
-#endif
-#if defined(GIT_SSH)
- | GIT_FEATURE_SSH
-#endif
-#if defined(GIT_USE_NSEC)
- | GIT_FEATURE_NSEC
-#endif
- ;
-}
-
-/* Declarations for tuneable settings */
-extern size_t git_mwindow__window_size;
-extern size_t git_mwindow__mapped_limit;
-
-static int config_level_to_sysdir(int config_level)
-{
- int val = -1;
-
- switch (config_level) {
- case GIT_CONFIG_LEVEL_SYSTEM:
- val = GIT_SYSDIR_SYSTEM;
- break;
- case GIT_CONFIG_LEVEL_XDG:
- val = GIT_SYSDIR_XDG;
- break;
- case GIT_CONFIG_LEVEL_GLOBAL:
- val = GIT_SYSDIR_GLOBAL;
- break;
- case GIT_CONFIG_LEVEL_PROGRAMDATA:
- val = GIT_SYSDIR_PROGRAMDATA;
- break;
- default:
- giterr_set(
- GITERR_INVALID, "Invalid config path selector %d", config_level);
- }
-
- return val;
-}
-
-extern char *git__user_agent;
-extern char *git__ssl_ciphers;
-
-const char *git_libgit2__user_agent(void)
-{
- return git__user_agent;
-}
-
-const char *git_libgit2__ssl_ciphers(void)
-{
- return git__ssl_ciphers;
-}
-
-int git_libgit2_opts(int key, ...)
-{
- int error = 0;
- va_list ap;
-
- va_start(ap, key);
-
- switch (key) {
- case GIT_OPT_SET_MWINDOW_SIZE:
- git_mwindow__window_size = va_arg(ap, size_t);
- break;
-
- case GIT_OPT_GET_MWINDOW_SIZE:
- *(va_arg(ap, size_t *)) = git_mwindow__window_size;
- break;
-
- case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT:
- git_mwindow__mapped_limit = va_arg(ap, size_t);
- break;
-
- case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT:
- *(va_arg(ap, size_t *)) = git_mwindow__mapped_limit;
- break;
-
- case GIT_OPT_GET_SEARCH_PATH:
- if ((error = config_level_to_sysdir(va_arg(ap, int))) >= 0) {
- git_buf *out = va_arg(ap, git_buf *);
- const git_buf *tmp;
-
- git_buf_sanitize(out);
- if ((error = git_sysdir_get(&tmp, error)) < 0)
- break;
-
- error = git_buf_sets(out, tmp->ptr);
- }
- break;
-
- case GIT_OPT_SET_SEARCH_PATH:
- if ((error = config_level_to_sysdir(va_arg(ap, int))) >= 0)
- error = git_sysdir_set(error, va_arg(ap, const char *));
- break;
-
- case GIT_OPT_SET_CACHE_OBJECT_LIMIT:
- {
- git_otype type = (git_otype)va_arg(ap, int);
- size_t size = va_arg(ap, size_t);
- error = git_cache_set_max_object_size(type, size);
- break;
- }
-
- case GIT_OPT_SET_CACHE_MAX_SIZE:
- git_cache__max_storage = va_arg(ap, ssize_t);
- break;
-
- case GIT_OPT_ENABLE_CACHING:
- git_cache__enabled = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_GET_CACHED_MEMORY:
- *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val;
- *(va_arg(ap, ssize_t *)) = git_cache__max_storage;
- break;
-
- case GIT_OPT_GET_TEMPLATE_PATH:
- {
- git_buf *out = va_arg(ap, git_buf *);
- const git_buf *tmp;
-
- git_buf_sanitize(out);
- if ((error = git_sysdir_get(&tmp, GIT_SYSDIR_TEMPLATE)) < 0)
- break;
-
- error = git_buf_sets(out, tmp->ptr);
- }
- break;
-
- case GIT_OPT_SET_TEMPLATE_PATH:
- error = git_sysdir_set(GIT_SYSDIR_TEMPLATE, va_arg(ap, const char *));
- break;
-
- case GIT_OPT_SET_SSL_CERT_LOCATIONS:
-#ifdef GIT_OPENSSL
- {
- const char *file = va_arg(ap, const char *);
- const char *path = va_arg(ap, const char *);
- if (!SSL_CTX_load_verify_locations(git__ssl_ctx, file, path)) {
- giterr_set(GITERR_NET, "SSL error: %s",
- ERR_error_string(ERR_get_error(), NULL));
- error = -1;
- }
- }
-#else
- giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL is not enabled");
- error = -1;
-#endif
- break;
- case GIT_OPT_SET_USER_AGENT:
- git__free(git__user_agent);
- git__user_agent = git__strdup(va_arg(ap, const char *));
- if (!git__user_agent) {
- giterr_set_oom();
- error = -1;
- }
-
- break;
-
- case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION:
- git_object__strict_input_validation = (va_arg(ap, int) != 0);
- break;
-
- case GIT_OPT_SET_SSL_CIPHERS:
-#ifdef GIT_OPENSSL
- {
- git__free(git__ssl_ciphers);
- git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
- if (!git__ssl_ciphers) {
- giterr_set_oom();
- error = -1;
- }
- }
-#else
- giterr_set(GITERR_NET, "cannot set custom ciphers: OpenSSL is not enabled");
- error = -1;
-#endif
- break;
-
- case GIT_OPT_GET_USER_AGENT:
- {
- git_buf *out = va_arg(ap, git_buf *);
- git_buf_sanitize(out);
- error = git_buf_sets(out, git__user_agent);
- }
- break;
-
- default:
- giterr_set(GITERR_INVALID, "invalid option key");
- error = -1;
- }
-
- va_end(ap);
-
- return error;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include <stdio.h>
-
-#include "sha1_lookup.h"
-#include "common.h"
-#include "oid.h"
-
-/*
- * Conventional binary search loop looks like this:
- *
- * unsigned lo, hi;
- * do {
- * unsigned mi = (lo + hi) / 2;
- * int cmp = "entry pointed at by mi" minus "target";
- * if (!cmp)
- * return (mi is the wanted one)
- * if (cmp > 0)
- * hi = mi; "mi is larger than target"
- * else
- * lo = mi+1; "mi is smaller than target"
- * } while (lo < hi);
- *
- * The invariants are:
- *
- * - When entering the loop, lo points at a slot that is never
- * above the target (it could be at the target), hi points at a
- * slot that is guaranteed to be above the target (it can never
- * be at the target).
- *
- * - We find a point 'mi' between lo and hi (mi could be the same
- * as lo, but never can be as same as hi), and check if it hits
- * the target. There are three cases:
- *
- * - if it is a hit, we are happy.
- *
- * - if it is strictly higher than the target, we set it to hi,
- * and repeat the search.
- *
- * - if it is strictly lower than the target, we update lo to
- * one slot after it, because we allow lo to be at the target.
- *
- * If the loop exits, there is no matching entry.
- *
- * When choosing 'mi', we do not have to take the "middle" but
- * anywhere in between lo and hi, as long as lo <= mi < hi is
- * satisfied. When we somehow know that the distance between the
- * target and lo is much shorter than the target and hi, we could
- * pick mi that is much closer to lo than the midway.
- *
- * Now, we can take advantage of the fact that SHA-1 is a good hash
- * function, and as long as there are enough entries in the table, we
- * can expect uniform distribution. An entry that begins with for
- * example "deadbeef..." is much likely to appear much later than in
- * the midway of the table. It can reasonably be expected to be near
- * 87% (222/256) from the top of the table.
- *
- * However, we do not want to pick "mi" too precisely. If the entry at
- * the 87% in the above example turns out to be higher than the target
- * we are looking for, we would end up narrowing the search space down
- * only by 13%, instead of 50% we would get if we did a simple binary
- * search. So we would want to hedge our bets by being less aggressive.
- *
- * The table at "table" holds at least "nr" entries of "elem_size"
- * bytes each. Each entry has the SHA-1 key at "key_offset". The
- * table is sorted by the SHA-1 key of the entries. The caller wants
- * to find the entry with "key", and knows that the entry at "lo" is
- * not higher than the entry it is looking for, and that the entry at
- * "hi" is higher than the entry it is looking for.
- */
-int sha1_entry_pos(const void *table,
- size_t elem_size,
- size_t key_offset,
- unsigned lo, unsigned hi, unsigned nr,
- const unsigned char *key)
-{
- const unsigned char *base = (const unsigned char*)table;
- const unsigned char *hi_key, *lo_key;
- unsigned ofs_0;
-
- if (!nr || lo >= hi)
- return -1;
-
- if (nr == hi)
- hi_key = NULL;
- else
- hi_key = base + elem_size * hi + key_offset;
- lo_key = base + elem_size * lo + key_offset;
-
- ofs_0 = 0;
- do {
- int cmp;
- unsigned ofs, mi, range;
- unsigned lov, hiv, kyv;
- const unsigned char *mi_key;
-
- range = hi - lo;
- if (hi_key) {
- for (ofs = ofs_0; ofs < 20; ofs++)
- if (lo_key[ofs] != hi_key[ofs])
- break;
- ofs_0 = ofs;
- /*
- * byte 0 thru (ofs-1) are the same between
- * lo and hi; ofs is the first byte that is
- * different.
- *
- * If ofs==20, then no bytes are different,
- * meaning we have entries with duplicate
- * keys. We know that we are in a solid run
- * of this entry (because the entries are
- * sorted, and our lo and hi are the same,
- * there can be nothing but this single key
- * in between). So we can stop the search.
- * Either one of these entries is it (and
- * we do not care which), or we do not have
- * it.
- *
- * Furthermore, we know that one of our
- * endpoints must be the edge of the run of
- * duplicates. For example, given this
- * sequence:
- *
- * idx 0 1 2 3 4 5
- * key A C C C C D
- *
- * If we are searching for "B", we might
- * hit the duplicate run at lo=1, hi=3
- * (e.g., by first mi=3, then mi=0). But we
- * can never have lo > 1, because B < C.
- * That is, if our key is less than the
- * run, we know that "lo" is the edge, but
- * we can say nothing of "hi". Similarly,
- * if our key is greater than the run, we
- * know that "hi" is the edge, but we can
- * say nothing of "lo".
- *
- * Therefore if we do not find it, we also
- * know where it would go if it did exist:
- * just on the far side of the edge that we
- * know about.
- */
- if (ofs == 20) {
- mi = lo;
- mi_key = base + elem_size * mi + key_offset;
- cmp = memcmp(mi_key, key, 20);
- if (!cmp)
- return mi;
- if (cmp < 0)
- return -1 - hi;
- else
- return -1 - lo;
- }
-
- hiv = hi_key[ofs_0];
- if (ofs_0 < 19)
- hiv = (hiv << 8) | hi_key[ofs_0+1];
- } else {
- hiv = 256;
- if (ofs_0 < 19)
- hiv <<= 8;
- }
- lov = lo_key[ofs_0];
- kyv = key[ofs_0];
- if (ofs_0 < 19) {
- lov = (lov << 8) | lo_key[ofs_0+1];
- kyv = (kyv << 8) | key[ofs_0+1];
- }
- assert(lov < hiv);
-
- if (kyv < lov)
- return -1 - lo;
- if (hiv < kyv)
- return -1 - hi;
-
- /*
- * Even if we know the target is much closer to 'hi'
- * than 'lo', if we pick too precisely and overshoot
- * (e.g. when we know 'mi' is closer to 'hi' than to
- * 'lo', pick 'mi' that is higher than the target), we
- * end up narrowing the search space by a smaller
- * amount (i.e. the distance between 'mi' and 'hi')
- * than what we would have (i.e. about half of 'lo'
- * and 'hi'). Hedge our bets to pick 'mi' less
- * aggressively, i.e. make 'mi' a bit closer to the
- * middle than we would otherwise pick.
- */
- kyv = (kyv * 6 + lov + hiv) / 8;
- if (lov < hiv - 1) {
- if (kyv == lov)
- kyv++;
- else if (kyv == hiv)
- kyv--;
- }
- mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;
-
-#ifdef INDEX_DEBUG_LOOKUP
- printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);
- printf("ofs %u lov %x, hiv %x, kyv %x\n",
- ofs_0, lov, hiv, kyv);
-#endif
-
- if (!(lo <= mi && mi < hi)) {
- giterr_set(GITERR_INVALID, "Assertion failure. Binary search invariant is false");
- return -1;
- }
-
- mi_key = base + elem_size * mi + key_offset;
- cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);
- if (!cmp)
- return mi;
- if (cmp > 0) {
- hi = mi;
- hi_key = mi_key;
- } else {
- lo = mi + 1;
- lo_key = mi_key + elem_size;
- }
- } while (lo < hi);
- return -((int)lo)-1;
-}
-
-int sha1_position(const void *table,
- size_t stride,
- unsigned lo, unsigned hi,
- const unsigned char *key)
-{
- const unsigned char *base = table;
-
- do {
- unsigned mi = (lo + hi) / 2;
- int cmp = git_oid__hashcmp(base + mi * stride, key);
-
- if (!cmp)
- return mi;
-
- if (cmp > 0)
- hi = mi;
- else
- lo = mi+1;
- } while (lo < hi);
-
- return -((int)lo)-1;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sha1_lookup_h__
-#define INCLUDE_sha1_lookup_h__
-
-#include <stdlib.h>
-
-int sha1_entry_pos(const void *table,
- size_t elem_size,
- size_t key_offset,
- unsigned lo, unsigned hi, unsigned nr,
- const unsigned char *key);
-
-int sha1_position(const void *table,
- size_t stride,
- unsigned lo, unsigned hi,
- const unsigned char *key);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "signature.h"
-#include "repository.h"
-#include "git2/common.h"
-#include "posix.h"
-
-void git_signature_free(git_signature *sig)
-{
- if (sig == NULL)
- return;
-
- git__free(sig->name);
- sig->name = NULL;
- git__free(sig->email);
- sig->email = NULL;
- git__free(sig);
-}
-
-static int signature_error(const char *msg)
-{
- giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg);
- return -1;
-}
-
-static bool contains_angle_brackets(const char *input)
-{
- return strchr(input, '<') != NULL || strchr(input, '>') != NULL;
-}
-
-static bool is_crud(unsigned char c)
-{
- return c <= 32 ||
- c == '.' ||
- c == ',' ||
- c == ':' ||
- c == ';' ||
- c == '<' ||
- c == '>' ||
- c == '"' ||
- c == '\\' ||
- c == '\'';
-}
-
-static char *extract_trimmed(const char *ptr, size_t len)
-{
- while (len && is_crud((unsigned char)ptr[0])) {
- ptr++; len--;
- }
-
- while (len && is_crud((unsigned char)ptr[len - 1])) {
- len--;
- }
-
- return git__substrdup(ptr, len);
-}
-
-int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)
-{
- git_signature *p = NULL;
-
- assert(name && email);
-
- *sig_out = NULL;
-
- if (contains_angle_brackets(name) ||
- contains_angle_brackets(email)) {
- return signature_error(
- "Neither `name` nor `email` should contain angle brackets chars.");
- }
-
- p = git__calloc(1, sizeof(git_signature));
- GITERR_CHECK_ALLOC(p);
-
- p->name = extract_trimmed(name, strlen(name));
- GITERR_CHECK_ALLOC(p->name);
- p->email = extract_trimmed(email, strlen(email));
- GITERR_CHECK_ALLOC(p->email);
-
- if (p->name[0] == '\0' || p->email[0] == '\0') {
- git_signature_free(p);
- return signature_error("Signature cannot have an empty name or email");
- }
-
- p->when.time = time;
- p->when.offset = offset;
-
- *sig_out = p;
- return 0;
-}
-
-int git_signature_dup(git_signature **dest, const git_signature *source)
-{
- git_signature *signature;
-
- if (source == NULL)
- return 0;
-
- signature = git__calloc(1, sizeof(git_signature));
- GITERR_CHECK_ALLOC(signature);
-
- signature->name = git__strdup(source->name);
- GITERR_CHECK_ALLOC(signature->name);
-
- signature->email = git__strdup(source->email);
- GITERR_CHECK_ALLOC(signature->email);
-
- signature->when.time = source->when.time;
- signature->when.offset = source->when.offset;
-
- *dest = signature;
-
- return 0;
-}
-
-int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool)
-{
- git_signature *signature;
-
- if (source == NULL)
- return 0;
-
- signature = git_pool_mallocz(pool, sizeof(git_signature));
- GITERR_CHECK_ALLOC(signature);
-
- signature->name = git_pool_strdup(pool, source->name);
- GITERR_CHECK_ALLOC(signature->name);
-
- signature->email = git_pool_strdup(pool, source->email);
- GITERR_CHECK_ALLOC(signature->email);
-
- signature->when.time = source->when.time;
- signature->when.offset = source->when.offset;
-
- *dest = signature;
-
- return 0;
-}
-
-int git_signature_now(git_signature **sig_out, const char *name, const char *email)
-{
- time_t now;
- time_t offset;
- struct tm *utc_tm;
- git_signature *sig;
- struct tm _utc;
-
- *sig_out = NULL;
-
- /*
- * Get the current time as seconds since the epoch and
- * transform that into a tm struct containing the time at
- * UTC. Give that to mktime which considers it a local time
- * (tm_isdst = -1 asks it to take DST into account) and gives
- * us that time as seconds since the epoch. The difference
- * between its return value and 'now' is our offset to UTC.
- */
- time(&now);
- utc_tm = p_gmtime_r(&now, &_utc);
- utc_tm->tm_isdst = -1;
- offset = (time_t)difftime(now, mktime(utc_tm));
- offset /= 60;
-
- if (git_signature_new(&sig, name, email, now, (int)offset) < 0)
- return -1;
-
- *sig_out = sig;
-
- return 0;
-}
-
-int git_signature_default(git_signature **out, git_repository *repo)
-{
- int error;
- git_config *cfg;
- const char *user_name, *user_email;
-
- if ((error = git_repository_config_snapshot(&cfg, repo)) < 0)
- return error;
-
- if (!(error = git_config_get_string(&user_name, cfg, "user.name")) &&
- !(error = git_config_get_string(&user_email, cfg, "user.email")))
- error = git_signature_now(out, user_name, user_email);
-
- git_config_free(cfg);
- return error;
-}
-
-int git_signature__parse(git_signature *sig, const char **buffer_out,
- const char *buffer_end, const char *header, char ender)
-{
- const char *buffer = *buffer_out;
- const char *email_start, *email_end;
-
- memset(sig, 0, sizeof(git_signature));
-
- if (ender &&
- (buffer_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
- return signature_error("no newline given");
-
- if (header) {
- const size_t header_len = strlen(header);
-
- if (buffer + header_len >= buffer_end || memcmp(buffer, header, header_len) != 0)
- return signature_error("expected prefix doesn't match actual");
-
- buffer += header_len;
- }
-
- email_start = git__memrchr(buffer, '<', buffer_end - buffer);
- email_end = git__memrchr(buffer, '>', buffer_end - buffer);
-
- if (!email_start || !email_end || email_end <= email_start)
- return signature_error("malformed e-mail");
-
- email_start += 1;
- sig->name = extract_trimmed(buffer, email_start - buffer - 1);
- sig->email = extract_trimmed(email_start, email_end - email_start);
-
- /* Do we even have a time at the end of the signature? */
- if (email_end + 2 < buffer_end) {
- const char *time_start = email_end + 2;
- const char *time_end;
-
- if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0)
- return signature_error("invalid Unix timestamp");
-
- /* do we have a timezone? */
- if (time_end + 1 < buffer_end) {
- int offset, hours, mins;
- const char *tz_start, *tz_end;
-
- tz_start = time_end + 1;
-
- if ((tz_start[0] != '-' && tz_start[0] != '+') ||
- git__strtol32(&offset, tz_start + 1, &tz_end, 10) < 0) {
- //malformed timezone, just assume it's zero
- offset = 0;
- }
-
- hours = offset / 100;
- mins = offset % 100;
-
- /*
- * only store timezone if it's not overflowing;
- * see http://www.worldtimezone.com/faq.html
- */
- if (hours <= 14 && mins <= 59) {
- sig->when.offset = (hours * 60) + mins;
- if (tz_start[0] == '-')
- sig->when.offset = -sig->when.offset;
- }
- }
- }
-
- *buffer_out = buffer_end + 1;
- return 0;
-}
-
-int git_signature_from_buffer(git_signature **out, const char *buf)
-{
- git_signature *sig;
- const char *buf_end;
- int error;
-
- assert(out && buf);
-
- *out = NULL;
-
- sig = git__calloc(1, sizeof(git_signature));
- GITERR_CHECK_ALLOC(sig);
-
- buf_end = buf + strlen(buf);
- error = git_signature__parse(sig, &buf, buf_end, NULL, '\0');
-
- if (error)
- git__free(sig);
- else
- *out = sig;
-
- return error;
-}
-
-void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig)
-{
- int offset, hours, mins;
- char sign;
-
- assert(buf && sig);
-
- offset = sig->when.offset;
- sign = (sig->when.offset < 0) ? '-' : '+';
-
- if (offset < 0)
- offset = -offset;
-
- hours = offset / 60;
- mins = offset % 60;
-
- git_buf_printf(buf, "%s%s <%s> %u %c%02d%02d\n",
- header ? header : "", sig->name, sig->email,
- (unsigned)sig->when.time, sign, hours, mins);
-}
-
-bool git_signature__equal(const git_signature *one, const git_signature *two)
-{
- assert(one && two);
-
- return
- git__strcmp(one->name, two->name) == 0 &&
- git__strcmp(one->email, two->email) == 0 &&
- one->when.time == two->when.time &&
- one->when.offset == two->when.offset;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_signature_h__
-#define INCLUDE_signature_h__
-
-#include "git2/common.h"
-#include "git2/signature.h"
-#include "repository.h"
-#include <time.h>
-
-int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender);
-void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig);
-bool git_signature__equal(const git_signature *one, const git_signature *two);
-
-int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "posix.h"
-#include "netops.h"
-#include "stream.h"
-#include "socket_stream.h"
-
-#ifndef _WIN32
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <sys/select.h>
-# include <sys/time.h>
-# include <netdb.h>
-# include <netinet/in.h>
-# include <arpa/inet.h>
-#else
-# include <winsock2.h>
-# include <ws2tcpip.h>
-# ifdef _MSC_VER
-# pragma comment(lib, "ws2_32")
-# endif
-#endif
-
-#ifdef GIT_WIN32
-static void net_set_error(const char *str)
-{
- int error = WSAGetLastError();
- char * win32_error = git_win32_get_error_message(error);
-
- if (win32_error) {
- giterr_set(GITERR_NET, "%s: %s", str, win32_error);
- git__free(win32_error);
- } else {
- giterr_set(GITERR_NET, str);
- }
-}
-#else
-static void net_set_error(const char *str)
-{
- giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
-}
-#endif
-
-static int close_socket(GIT_SOCKET s)
-{
- if (s == INVALID_SOCKET)
- return 0;
-
-#ifdef GIT_WIN32
- if (SOCKET_ERROR == closesocket(s))
- return -1;
-
- if (0 != WSACleanup()) {
- giterr_set(GITERR_OS, "Winsock cleanup failed");
- return -1;
- }
-
- return 0;
-#else
- return close(s);
-#endif
-
-}
-
-int socket_connect(git_stream *stream)
-{
- struct addrinfo *info = NULL, *p;
- struct addrinfo hints;
- git_socket_stream *st = (git_socket_stream *) stream;
- GIT_SOCKET s = INVALID_SOCKET;
- int ret;
-
-#ifdef GIT_WIN32
- /* on win32, the WSA context needs to be initialized
- * before any socket calls can be performed */
- WSADATA wsd;
-
- if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
- giterr_set(GITERR_OS, "Winsock init failed");
- return -1;
- }
-
- if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
- WSACleanup();
- giterr_set(GITERR_OS, "Winsock init failed");
- return -1;
- }
-#endif
-
- memset(&hints, 0x0, sizeof(struct addrinfo));
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_family = AF_UNSPEC;
-
- if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
- giterr_set(GITERR_NET,
- "Failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
- return -1;
- }
-
- for (p = info; p != NULL; p = p->ai_next) {
- s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
-
- if (s == INVALID_SOCKET) {
- net_set_error("error creating socket");
- break;
- }
-
- if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
- break;
-
- /* If we can't connect, try the next one */
- close_socket(s);
- s = INVALID_SOCKET;
- }
-
- /* Oops, we couldn't connect to any address */
- if (s == INVALID_SOCKET && p == NULL) {
- giterr_set(GITERR_OS, "Failed to connect to %s", st->host);
- p_freeaddrinfo(info);
- return -1;
- }
-
- st->s = s;
- p_freeaddrinfo(info);
- return 0;
-}
-
-ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
-{
- ssize_t ret;
- size_t off = 0;
- git_socket_stream *st = (git_socket_stream *) stream;
-
- while (off < len) {
- errno = 0;
- ret = p_send(st->s, data + off, len - off, flags);
- if (ret < 0) {
- net_set_error("Error sending data");
- return -1;
- }
-
- off += ret;
- }
-
- return off;
-}
-
-ssize_t socket_read(git_stream *stream, void *data, size_t len)
-{
- ssize_t ret;
- git_socket_stream *st = (git_socket_stream *) stream;
-
- if ((ret = p_recv(st->s, data, len, 0)) < 0)
- net_set_error("Error receiving socket data");
-
- return ret;
-}
-
-int socket_close(git_stream *stream)
-{
- git_socket_stream *st = (git_socket_stream *) stream;
- int error;
-
- error = close_socket(st->s);
- st->s = INVALID_SOCKET;
-
- return error;
-}
-
-void socket_free(git_stream *stream)
-{
- git_socket_stream *st = (git_socket_stream *) stream;
-
- git__free(st->host);
- git__free(st->port);
- git__free(st);
-}
-
-int git_socket_stream_new(git_stream **out, const char *host, const char *port)
-{
- git_socket_stream *st;
-
- assert(out && host);
-
- st = git__calloc(1, sizeof(git_socket_stream));
- GITERR_CHECK_ALLOC(st);
-
- st->host = git__strdup(host);
- GITERR_CHECK_ALLOC(st->host);
-
- if (port) {
- st->port = git__strdup(port);
- GITERR_CHECK_ALLOC(st->port);
- }
-
- st->parent.version = GIT_STREAM_VERSION;
- st->parent.connect = socket_connect;
- st->parent.write = socket_write;
- st->parent.read = socket_read;
- st->parent.close = socket_close;
- st->parent.free = socket_free;
- st->s = INVALID_SOCKET;
-
- *out = (git_stream *) st;
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_socket_stream_h__
-#define INCLUDE_socket_stream_h__
-
-#include "netops.h"
-
-typedef struct {
- git_stream parent;
- char *host;
- char *port;
- GIT_SOCKET s;
-} git_socket_stream;
-
-extern int git_socket_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
+++ /dev/null
-#include "sortedcache.h"
-
-GIT__USE_STRMAP
-
-int git_sortedcache_new(
- git_sortedcache **out,
- size_t item_path_offset,
- git_sortedcache_free_item_fn free_item,
- void *free_item_payload,
- git_vector_cmp item_cmp,
- const char *path)
-{
- git_sortedcache *sc;
- size_t pathlen, alloclen;
-
- pathlen = path ? strlen(path) : 0;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_sortedcache), pathlen);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
- sc = git__calloc(1, alloclen);
- GITERR_CHECK_ALLOC(sc);
-
- git_pool_init(&sc->pool, 1);
-
- if (git_vector_init(&sc->items, 4, item_cmp) < 0 ||
- git_strmap_alloc(&sc->map) < 0)
- goto fail;
-
- if (git_rwlock_init(&sc->lock)) {
- giterr_set(GITERR_OS, "Failed to initialize lock");
- goto fail;
- }
-
- sc->item_path_offset = item_path_offset;
- sc->free_item = free_item;
- sc->free_item_payload = free_item_payload;
- GIT_REFCOUNT_INC(sc);
- if (pathlen)
- memcpy(sc->path, path, pathlen);
-
- *out = sc;
- return 0;
-
-fail:
- git_strmap_free(sc->map);
- git_vector_free(&sc->items);
- git_pool_clear(&sc->pool);
- git__free(sc);
- return -1;
-}
-
-void git_sortedcache_incref(git_sortedcache *sc)
-{
- GIT_REFCOUNT_INC(sc);
-}
-
-const char *git_sortedcache_path(git_sortedcache *sc)
-{
- return sc->path;
-}
-
-static void sortedcache_clear(git_sortedcache *sc)
-{
- git_strmap_clear(sc->map);
-
- if (sc->free_item) {
- size_t i;
- void *item;
-
- git_vector_foreach(&sc->items, i, item) {
- sc->free_item(sc->free_item_payload, item);
- }
- }
-
- git_vector_clear(&sc->items);
-
- git_pool_clear(&sc->pool);
-}
-
-static void sortedcache_free(git_sortedcache *sc)
-{
- /* acquire write lock to make sure everyone else is done */
- if (git_sortedcache_wlock(sc) < 0)
- return;
-
- sortedcache_clear(sc);
- git_vector_free(&sc->items);
- git_strmap_free(sc->map);
-
- git_sortedcache_wunlock(sc);
-
- git_rwlock_free(&sc->lock);
- git__free(sc);
-}
-
-void git_sortedcache_free(git_sortedcache *sc)
-{
- if (!sc)
- return;
- GIT_REFCOUNT_DEC(sc, sortedcache_free);
-}
-
-static int sortedcache_copy_item(void *payload, void *tgt_item, void *src_item)
-{
- git_sortedcache *sc = payload;
- /* path will already have been copied by upsert */
- memcpy(tgt_item, src_item, sc->item_path_offset);
- return 0;
-}
-
-/* copy a sorted cache */
-int git_sortedcache_copy(
- git_sortedcache **out,
- git_sortedcache *src,
- bool lock,
- int (*copy_item)(void *payload, void *tgt_item, void *src_item),
- void *payload)
-{
- int error = 0;
- git_sortedcache *tgt;
- size_t i;
- void *src_item, *tgt_item;
-
- /* just use memcpy if no special copy fn is passed in */
- if (!copy_item) {
- copy_item = sortedcache_copy_item;
- payload = src;
- }
-
- if ((error = git_sortedcache_new(
- &tgt, src->item_path_offset,
- src->free_item, src->free_item_payload,
- src->items._cmp, src->path)) < 0)
- return error;
-
- if (lock && git_sortedcache_rlock(src) < 0) {
- git_sortedcache_free(tgt);
- return -1;
- }
-
- git_vector_foreach(&src->items, i, src_item) {
- char *path = ((char *)src_item) + src->item_path_offset;
-
- if ((error = git_sortedcache_upsert(&tgt_item, tgt, path)) < 0 ||
- (error = copy_item(payload, tgt_item, src_item)) < 0)
- break;
- }
-
- if (lock)
- git_sortedcache_runlock(src);
- if (error)
- git_sortedcache_free(tgt);
-
- *out = !error ? tgt : NULL;
-
- return error;
-}
-
-/* lock sortedcache while making modifications */
-int git_sortedcache_wlock(git_sortedcache *sc)
-{
- GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
-
- if (git_rwlock_wrlock(&sc->lock) < 0) {
- giterr_set(GITERR_OS, "Unable to acquire write lock on cache");
- return -1;
- }
- return 0;
-}
-
-/* unlock sorted cache when done with modifications */
-void git_sortedcache_wunlock(git_sortedcache *sc)
-{
- git_vector_sort(&sc->items);
- git_rwlock_wrunlock(&sc->lock);
-}
-
-/* lock sortedcache for read */
-int git_sortedcache_rlock(git_sortedcache *sc)
-{
- GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
-
- if (git_rwlock_rdlock(&sc->lock) < 0) {
- giterr_set(GITERR_OS, "Unable to acquire read lock on cache");
- return -1;
- }
- return 0;
-}
-
-/* unlock sorted cache when done reading */
-void git_sortedcache_runlock(git_sortedcache *sc)
-{
- GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
- git_rwlock_rdunlock(&sc->lock);
-}
-
-/* if the file has changed, lock cache and load file contents into buf;
- * returns <0 on error, >0 if file has not changed
- */
-int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf)
-{
- int error, fd;
- struct stat st;
-
- if ((error = git_sortedcache_wlock(sc)) < 0)
- return error;
-
- if ((error = git_futils_filestamp_check(&sc->stamp, sc->path)) <= 0)
- goto unlock;
-
- if ((fd = git_futils_open_ro(sc->path)) < 0) {
- error = fd;
- goto unlock;
- }
-
- if (p_fstat(fd, &st) < 0) {
- giterr_set(GITERR_OS, "failed to stat file");
- error = -1;
- (void)p_close(fd);
- goto unlock;
- }
-
- if (!git__is_sizet(st.st_size)) {
- giterr_set(GITERR_INVALID, "Unable to load file larger than size_t");
- error = -1;
- (void)p_close(fd);
- goto unlock;
- }
-
- if (buf)
- error = git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size);
-
- (void)p_close(fd);
-
- if (error < 0)
- goto unlock;
-
- return 1; /* return 1 -> file needs reload and was successfully loaded */
-
-unlock:
- git_sortedcache_wunlock(sc);
- return error;
-}
-
-void git_sortedcache_updated(git_sortedcache *sc)
-{
- /* update filestamp to latest value */
- git_futils_filestamp_check(&sc->stamp, sc->path);
-}
-
-/* release all items in sorted cache */
-int git_sortedcache_clear(git_sortedcache *sc, bool wlock)
-{
- if (wlock && git_sortedcache_wlock(sc) < 0)
- return -1;
-
- sortedcache_clear(sc);
-
- if (wlock)
- git_sortedcache_wunlock(sc);
-
- return 0;
-}
-
-/* find and/or insert item, returning pointer to item data */
-int git_sortedcache_upsert(void **out, git_sortedcache *sc, const char *key)
-{
- int error = 0;
- khiter_t pos;
- void *item;
- size_t keylen, itemlen;
- char *item_key;
-
- pos = git_strmap_lookup_index(sc->map, key);
- if (git_strmap_valid_index(sc->map, pos)) {
- item = git_strmap_value_at(sc->map, pos);
- goto done;
- }
-
- keylen = strlen(key);
- itemlen = sc->item_path_offset + keylen + 1;
- itemlen = (itemlen + 7) & ~7;
-
- if ((item = git_pool_mallocz(&sc->pool, (uint32_t)itemlen)) == NULL) {
- /* don't use GITERR_CHECK_ALLOC b/c of lock */
- error = -1;
- goto done;
- }
-
- /* one strange thing is that even if the vector or hash table insert
- * fail, there is no way to free the pool item so we just abandon it
- */
-
- item_key = ((char *)item) + sc->item_path_offset;
- memcpy(item_key, key, keylen);
-
- pos = kh_put(str, sc->map, item_key, &error);
- if (error < 0)
- goto done;
-
- if (!error)
- kh_key(sc->map, pos) = item_key;
- kh_val(sc->map, pos) = item;
-
- error = git_vector_insert(&sc->items, item);
- if (error < 0)
- git_strmap_delete_at(sc->map, pos);
-
-done:
- if (out)
- *out = !error ? item : NULL;
- return error;
-}
-
-/* lookup item by key */
-void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key)
-{
- khiter_t pos = git_strmap_lookup_index(sc->map, key);
- if (git_strmap_valid_index(sc->map, pos))
- return git_strmap_value_at(sc->map, pos);
- return NULL;
-}
-
-/* find out how many items are in the cache */
-size_t git_sortedcache_entrycount(const git_sortedcache *sc)
-{
- return git_vector_length(&sc->items);
-}
-
-/* lookup item by index */
-void *git_sortedcache_entry(git_sortedcache *sc, size_t pos)
-{
- /* make sure the items are sorted so this gets the correct item */
- if (!git_vector_is_sorted(&sc->items))
- git_vector_sort(&sc->items);
-
- return git_vector_get(&sc->items, pos);
-}
-
-/* helper struct so bsearch callback can know offset + key value for cmp */
-struct sortedcache_magic_key {
- size_t offset;
- const char *key;
-};
-
-static int sortedcache_magic_cmp(const void *key, const void *value)
-{
- const struct sortedcache_magic_key *magic = key;
- const char *value_key = ((const char *)value) + magic->offset;
- return strcmp(magic->key, value_key);
-}
-
-/* lookup index of item by key */
-int git_sortedcache_lookup_index(
- size_t *out, git_sortedcache *sc, const char *key)
-{
- struct sortedcache_magic_key magic;
-
- magic.offset = sc->item_path_offset;
- magic.key = key;
-
- return git_vector_bsearch2(out, &sc->items, sortedcache_magic_cmp, &magic);
-}
-
-/* remove entry from cache */
-int git_sortedcache_remove(git_sortedcache *sc, size_t pos)
-{
- char *item;
- khiter_t mappos;
-
- /* because of pool allocation, this can't actually remove the item,
- * but we can remove it from the items vector and the hash table.
- */
-
- if ((item = git_vector_get(&sc->items, pos)) == NULL) {
- giterr_set(GITERR_INVALID, "Removing item out of range");
- return GIT_ENOTFOUND;
- }
-
- (void)git_vector_remove(&sc->items, pos);
-
- mappos = git_strmap_lookup_index(sc->map, item + sc->item_path_offset);
- git_strmap_delete_at(sc->map, mappos);
-
- if (sc->free_item)
- sc->free_item(sc->free_item_payload, item);
-
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sorted_cache_h__
-#define INCLUDE_sorted_cache_h__
-
-#include "util.h"
-#include "fileops.h"
-#include "vector.h"
-#include "thread-utils.h"
-#include "pool.h"
-#include "strmap.h"
-
-#include <stddef.h>
-
-/*
- * The purpose of this data structure is to cache the parsed contents of a
- * file (a.k.a. the backing file) where each item in the file can be
- * identified by a key string and you want to both look them up by name
- * and traverse them in sorted order. Each item is assumed to itself end
- * in a GIT_FLEX_ARRAY.
- */
-
-typedef void (*git_sortedcache_free_item_fn)(void *payload, void *item);
-
-typedef struct {
- git_refcount rc;
- git_rwlock lock;
- size_t item_path_offset;
- git_sortedcache_free_item_fn free_item;
- void *free_item_payload;
- git_pool pool;
- git_vector items;
- git_strmap *map;
- git_futils_filestamp stamp;
- char path[GIT_FLEX_ARRAY];
-} git_sortedcache;
-
-/* Create a new sortedcache
- *
- * Even though every sortedcache stores items with a GIT_FLEX_ARRAY at
- * the end containing their key string, you have to provide the item_cmp
- * sorting function because the sorting function doesn't get a payload
- * and therefore can't know the offset to the item key string. :-(
- *
- * @param out The allocated git_sortedcache
- * @param item_path_offset Offset to the GIT_FLEX_ARRAY item key in the
- * struct - use offsetof(struct mine, key-field) to get this
- * @param free_item Optional callback to free each item
- * @param free_item_payload Optional payload passed to free_item callback
- * @param item_cmp Compare the keys of two items
- * @param path The path to the backing store file for this cache; this
- * may be NULL. The cache makes it easy to load this and check
- * if it has been modified since the last load and/or write.
- */
-int git_sortedcache_new(
- git_sortedcache **out,
- size_t item_path_offset, /* use offsetof(struct, path-field) macro */
- git_sortedcache_free_item_fn free_item,
- void *free_item_payload,
- git_vector_cmp item_cmp,
- const char *path);
-
-/* Copy a sorted cache
- *
- * - `copy_item` can be NULL to just use memcpy
- * - if `lock`, grabs read lock on `src` during copy and releases after
- */
-int git_sortedcache_copy(
- git_sortedcache **out,
- git_sortedcache *src,
- bool lock,
- int (*copy_item)(void *payload, void *tgt_item, void *src_item),
- void *payload);
-
-/* Free sorted cache (first calling `free_item` callbacks)
- *
- * Don't call on a locked collection - it may acquire a write lock
- */
-void git_sortedcache_free(git_sortedcache *sc);
-
-/* Increment reference count - balance with call to free */
-void git_sortedcache_incref(git_sortedcache *sc);
-
-/* Get the pathname associated with this cache at creation time */
-const char *git_sortedcache_path(git_sortedcache *sc);
-
-/*
- * CACHE WRITE FUNCTIONS
- *
- * The following functions require you to have a writer lock to make the
- * modification. Some of the functions take a `wlock` parameter and
- * will optionally lock and unlock for you if that is passed as true.
- *
- */
-
-/* Lock sortedcache for write */
-int git_sortedcache_wlock(git_sortedcache *sc);
-
-/* Unlock sorted cache when done with write */
-void git_sortedcache_wunlock(git_sortedcache *sc);
-
-/* Lock cache and load backing file into a buffer.
- *
- * This grabs a write lock on the cache then looks at the modification
- * time and size of the file on disk.
- *
- * If the file appears to have changed, this loads the file contents into
- * the buffer and returns a positive value leaving the cache locked - the
- * caller should parse the file content, update the cache as needed, then
- * release the lock. NOTE: In this case, the caller MUST unlock the cache.
- *
- * If the file appears to be unchanged, then this automatically releases
- * the lock on the cache, clears the buffer, and returns 0.
- *
- * @return 0 if up-to-date, 1 if out-of-date, <0 on error
- */
-int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf);
-
-/* Refresh file timestamp after write completes
- * You should already be holding the write lock when you call this.
- */
-void git_sortedcache_updated(git_sortedcache *sc);
-
-/* Release all items in sorted cache
- *
- * If `wlock` is true, grabs write lock and releases when done, otherwise
- * you should already be holding a write lock when you call this.
- */
-int git_sortedcache_clear(git_sortedcache *sc, bool wlock);
-
-/* Find and/or insert item, returning pointer to item data.
- * You should already be holding the write lock when you call this.
- */
-int git_sortedcache_upsert(
- void **out, git_sortedcache *sc, const char *key);
-
-/* Removes entry at pos from cache
- * You should already be holding the write lock when you call this.
- */
-int git_sortedcache_remove(git_sortedcache *sc, size_t pos);
-
-/*
- * CACHE READ FUNCTIONS
- *
- * The following functions access items in the cache. To prevent the
- * results from being invalidated before they can be used, you should be
- * holding either a read lock or a write lock when using these functions.
- *
- */
-
-/* Lock sortedcache for read */
-int git_sortedcache_rlock(git_sortedcache *sc);
-
-/* Unlock sorted cache when done with read */
-void git_sortedcache_runlock(git_sortedcache *sc);
-
-/* Lookup item by key - returns NULL if not found */
-void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key);
-
-/* Get how many items are in the cache
- *
- * You can call this function without holding a lock, but be aware
- * that it may change before you use it.
- */
-size_t git_sortedcache_entrycount(const git_sortedcache *sc);
-
-/* Lookup item by index - returns NULL if out of range */
-void *git_sortedcache_entry(git_sortedcache *sc, size_t pos);
-
-/* Lookup index of item by key - returns GIT_ENOTFOUND if not found */
-int git_sortedcache_lookup_index(
- size_t *out, git_sortedcache *sc, const char *key);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "repository.h"
-#include "commit.h"
-#include "message.h"
-#include "tree.h"
-#include "reflog.h"
-#include "git2/diff.h"
-#include "git2/stash.h"
-#include "git2/status.h"
-#include "git2/checkout.h"
-#include "git2/index.h"
-#include "git2/transaction.h"
-#include "git2/merge.h"
-#include "index.h"
-#include "signature.h"
-#include "iterator.h"
-#include "merge.h"
-#include "diff.h"
-#include "diff_generate.h"
-
-static int create_error(int error, const char *msg)
-{
- giterr_set(GITERR_STASH, "Cannot stash changes - %s", msg);
- return error;
-}
-
-static int retrieve_head(git_reference **out, git_repository *repo)
-{
- int error = git_repository_head(out, repo);
-
- if (error == GIT_EUNBORNBRANCH)
- return create_error(error, "You do not have the initial commit yet.");
-
- return error;
-}
-
-static int append_abbreviated_oid(git_buf *out, const git_oid *b_commit)
-{
- char *formatted_oid;
-
- formatted_oid = git_oid_allocfmt(b_commit);
- GITERR_CHECK_ALLOC(formatted_oid);
-
- git_buf_put(out, formatted_oid, 7);
- git__free(formatted_oid);
-
- return git_buf_oom(out) ? -1 : 0;
-}
-
-static int append_commit_description(git_buf *out, git_commit* commit)
-{
- const char *summary = git_commit_summary(commit);
- GITERR_CHECK_ALLOC(summary);
-
- if (append_abbreviated_oid(out, git_commit_id(commit)) < 0)
- return -1;
-
- git_buf_putc(out, ' ');
- git_buf_puts(out, summary);
- git_buf_putc(out, '\n');
-
- return git_buf_oom(out) ? -1 : 0;
-}
-
-static int retrieve_base_commit_and_message(
- git_commit **b_commit,
- git_buf *stash_message,
- git_repository *repo)
-{
- git_reference *head = NULL;
- int error;
-
- if ((error = retrieve_head(&head, repo)) < 0)
- return error;
-
- if (strcmp("HEAD", git_reference_name(head)) == 0)
- error = git_buf_puts(stash_message, "(no branch): ");
- else
- error = git_buf_printf(
- stash_message,
- "%s: ",
- git_reference_name(head) + strlen(GIT_REFS_HEADS_DIR));
- if (error < 0)
- goto cleanup;
-
- if ((error = git_commit_lookup(
- b_commit, repo, git_reference_target(head))) < 0)
- goto cleanup;
-
- if ((error = append_commit_description(stash_message, *b_commit)) < 0)
- goto cleanup;
-
-cleanup:
- git_reference_free(head);
- return error;
-}
-
-static int build_tree_from_index(git_tree **out, git_index *index)
-{
- int error;
- git_oid i_tree_oid;
-
- if ((error = git_index_write_tree(&i_tree_oid, index)) < 0)
- return error;
-
- return git_tree_lookup(out, git_index_owner(index), &i_tree_oid);
-}
-
-static int commit_index(
- git_commit **i_commit,
- git_index *index,
- const git_signature *stasher,
- const char *message,
- const git_commit *parent)
-{
- git_tree *i_tree = NULL;
- git_oid i_commit_oid;
- git_buf msg = GIT_BUF_INIT;
- int error;
-
- if ((error = build_tree_from_index(&i_tree, index)) < 0)
- goto cleanup;
-
- if ((error = git_buf_printf(&msg, "index on %s\n", message)) < 0)
- goto cleanup;
-
- if ((error = git_commit_create(
- &i_commit_oid,
- git_index_owner(index),
- NULL,
- stasher,
- stasher,
- NULL,
- git_buf_cstr(&msg),
- i_tree,
- 1,
- &parent)) < 0)
- goto cleanup;
-
- error = git_commit_lookup(i_commit, git_index_owner(index), &i_commit_oid);
-
-cleanup:
- git_tree_free(i_tree);
- git_buf_free(&msg);
- return error;
-}
-
-struct stash_update_rules {
- bool include_changed;
- bool include_untracked;
- bool include_ignored;
-};
-
-static int stash_update_index_from_diff(
- git_index *index,
- const git_diff *diff,
- struct stash_update_rules *data)
-{
- int error = 0;
- size_t d, max_d = git_diff_num_deltas(diff);
-
- for (d = 0; !error && d < max_d; ++d) {
- const char *add_path = NULL;
- const git_diff_delta *delta = git_diff_get_delta(diff, d);
-
- switch (delta->status) {
- case GIT_DELTA_IGNORED:
- if (data->include_ignored)
- add_path = delta->new_file.path;
- break;
-
- case GIT_DELTA_UNTRACKED:
- if (data->include_untracked &&
- delta->new_file.mode != GIT_FILEMODE_TREE)
- add_path = delta->new_file.path;
- break;
-
- case GIT_DELTA_ADDED:
- case GIT_DELTA_MODIFIED:
- if (data->include_changed)
- add_path = delta->new_file.path;
- break;
-
- case GIT_DELTA_DELETED:
- if (data->include_changed &&
- !git_index_find(NULL, index, delta->old_file.path))
- error = git_index_remove(index, delta->old_file.path, 0);
- break;
-
- default:
- /* Unimplemented */
- giterr_set(
- GITERR_INVALID,
- "Cannot update index. Unimplemented status (%d)",
- delta->status);
- return -1;
- }
-
- if (add_path != NULL)
- error = git_index_add_bypath(index, add_path);
- }
-
- return error;
-}
-
-static int build_untracked_tree(
- git_tree **tree_out,
- git_index *index,
- git_commit *i_commit,
- uint32_t flags)
-{
- git_tree *i_tree = NULL;
- git_diff *diff = NULL;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- struct stash_update_rules data = {0};
- int error;
-
- git_index_clear(index);
-
- if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
- opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
- GIT_DIFF_RECURSE_UNTRACKED_DIRS;
- data.include_untracked = true;
- }
-
- if (flags & GIT_STASH_INCLUDE_IGNORED) {
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED |
- GIT_DIFF_RECURSE_IGNORED_DIRS;
- data.include_ignored = true;
- }
-
- if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
- goto cleanup;
-
- if ((error = git_diff_tree_to_workdir(
- &diff, git_index_owner(index), i_tree, &opts)) < 0)
- goto cleanup;
-
- if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
- goto cleanup;
-
- error = build_tree_from_index(tree_out, index);
-
-cleanup:
- git_diff_free(diff);
- git_tree_free(i_tree);
- return error;
-}
-
-static int commit_untracked(
- git_commit **u_commit,
- git_index *index,
- const git_signature *stasher,
- const char *message,
- git_commit *i_commit,
- uint32_t flags)
-{
- git_tree *u_tree = NULL;
- git_oid u_commit_oid;
- git_buf msg = GIT_BUF_INIT;
- int error;
-
- if ((error = build_untracked_tree(&u_tree, index, i_commit, flags)) < 0)
- goto cleanup;
-
- if ((error = git_buf_printf(&msg, "untracked files on %s\n", message)) < 0)
- goto cleanup;
-
- if ((error = git_commit_create(
- &u_commit_oid,
- git_index_owner(index),
- NULL,
- stasher,
- stasher,
- NULL,
- git_buf_cstr(&msg),
- u_tree,
- 0,
- NULL)) < 0)
- goto cleanup;
-
- error = git_commit_lookup(u_commit, git_index_owner(index), &u_commit_oid);
-
-cleanup:
- git_tree_free(u_tree);
- git_buf_free(&msg);
- return error;
-}
-
-static git_diff_delta *stash_delta_merge(
- const git_diff_delta *a,
- const git_diff_delta *b,
- git_pool *pool)
-{
- /* Special case for stash: if a file is deleted in the index, but exists
- * in the working tree, we need to stash the workdir copy for the workdir.
- */
- if (a->status == GIT_DELTA_DELETED && b->status == GIT_DELTA_UNTRACKED) {
- git_diff_delta *dup = git_diff__delta_dup(b, pool);
-
- if (dup)
- dup->status = GIT_DELTA_MODIFIED;
- return dup;
- }
-
- return git_diff__merge_like_cgit(a, b, pool);
-}
-
-static int build_workdir_tree(
- git_tree **tree_out,
- git_index *index,
- git_commit *b_commit)
-{
- git_repository *repo = git_index_owner(index);
- git_tree *b_tree = NULL;
- git_diff *diff = NULL, *idx_to_wd = NULL;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- struct stash_update_rules data = {0};
- int error;
-
- opts.flags = GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_INCLUDE_UNTRACKED;
-
- if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
- goto cleanup;
-
- if ((error = git_diff_tree_to_index(&diff, repo, b_tree, index, &opts)) < 0 ||
- (error = git_diff_index_to_workdir(&idx_to_wd, repo, index, &opts)) < 0 ||
- (error = git_diff__merge(diff, idx_to_wd, stash_delta_merge)) < 0)
- goto cleanup;
-
- data.include_changed = true;
-
- if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
- goto cleanup;
-
- error = build_tree_from_index(tree_out, index);
-
-cleanup:
- git_diff_free(idx_to_wd);
- git_diff_free(diff);
- git_tree_free(b_tree);
-
- return error;
-}
-
-static int commit_worktree(
- git_oid *w_commit_oid,
- git_index *index,
- const git_signature *stasher,
- const char *message,
- git_commit *i_commit,
- git_commit *b_commit,
- git_commit *u_commit)
-{
- int error = 0;
- git_tree *w_tree = NULL, *i_tree = NULL;
- const git_commit *parents[] = { NULL, NULL, NULL };
-
- parents[0] = b_commit;
- parents[1] = i_commit;
- parents[2] = u_commit;
-
- if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
- goto cleanup;
-
- if ((error = git_index_read_tree(index, i_tree)) < 0)
- goto cleanup;
-
- if ((error = build_workdir_tree(&w_tree, index, b_commit)) < 0)
- goto cleanup;
-
- error = git_commit_create(
- w_commit_oid,
- git_index_owner(index),
- NULL,
- stasher,
- stasher,
- NULL,
- message,
- w_tree,
- u_commit ? 3 : 2,
- parents);
-
-cleanup:
- git_tree_free(i_tree);
- git_tree_free(w_tree);
- return error;
-}
-
-static int prepare_worktree_commit_message(
- git_buf* msg,
- const char *user_message)
-{
- git_buf buf = GIT_BUF_INIT;
- int error;
-
- if ((error = git_buf_set(&buf, git_buf_cstr(msg), git_buf_len(msg))) < 0)
- return error;
-
- git_buf_clear(msg);
-
- if (!user_message)
- git_buf_printf(msg, "WIP on %s", git_buf_cstr(&buf));
- else {
- const char *colon;
-
- if ((colon = strchr(git_buf_cstr(&buf), ':')) == NULL)
- goto cleanup;
-
- git_buf_puts(msg, "On ");
- git_buf_put(msg, git_buf_cstr(&buf), colon - buf.ptr);
- git_buf_printf(msg, ": %s\n", user_message);
- }
-
- error = (git_buf_oom(msg) || git_buf_oom(&buf)) ? -1 : 0;
-
-cleanup:
- git_buf_free(&buf);
-
- return error;
-}
-
-static int update_reflog(
- git_oid *w_commit_oid,
- git_repository *repo,
- const char *message)
-{
- git_reference *stash;
- int error;
-
- if ((error = git_reference_ensure_log(repo, GIT_REFS_STASH_FILE)) < 0)
- return error;
-
- error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1, message);
-
- git_reference_free(stash);
-
- return error;
-}
-
-static int is_dirty_cb(const char *path, unsigned int status, void *payload)
-{
- GIT_UNUSED(path);
- GIT_UNUSED(status);
- GIT_UNUSED(payload);
-
- return GIT_PASSTHROUGH;
-}
-
-static int ensure_there_are_changes_to_stash(
- git_repository *repo,
- bool include_untracked_files,
- bool include_ignored_files)
-{
- int error;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
-
- opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
- opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
-
- if (include_untracked_files)
- opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
-
- if (include_ignored_files)
- opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED |
- GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
-
- error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
-
- if (error == GIT_PASSTHROUGH)
- return 0;
-
- if (!error)
- return create_error(GIT_ENOTFOUND, "There is nothing to stash.");
-
- return error;
-}
-
-static int reset_index_and_workdir(
- git_repository *repo,
- git_commit *commit,
- bool remove_untracked,
- bool remove_ignored)
-{
- git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- if (remove_untracked)
- opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
-
- if (remove_ignored)
- opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_IGNORED;
-
- return git_checkout_tree(repo, (git_object *)commit, &opts);
-}
-
-int git_stash_save(
- git_oid *out,
- git_repository *repo,
- const git_signature *stasher,
- const char *message,
- uint32_t flags)
-{
- git_index *index = NULL;
- git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL;
- git_buf msg = GIT_BUF_INIT;
- int error;
-
- assert(out && repo && stasher);
-
- if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0)
- return error;
-
- if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
- goto cleanup;
-
- if ((error = ensure_there_are_changes_to_stash(
- repo,
- (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0,
- (flags & GIT_STASH_INCLUDE_IGNORED) != 0)) < 0)
- goto cleanup;
-
- if ((error = git_repository_index(&index, repo)) < 0)
- goto cleanup;
-
- if ((error = commit_index(
- &i_commit, index, stasher, git_buf_cstr(&msg), b_commit)) < 0)
- goto cleanup;
-
- if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
- (error = commit_untracked(
- &u_commit, index, stasher, git_buf_cstr(&msg),
- i_commit, flags)) < 0)
- goto cleanup;
-
- if ((error = prepare_worktree_commit_message(&msg, message)) < 0)
- goto cleanup;
-
- if ((error = commit_worktree(
- out, index, stasher, git_buf_cstr(&msg),
- i_commit, b_commit, u_commit)) < 0)
- goto cleanup;
-
- git_buf_rtrim(&msg);
-
- if ((error = update_reflog(out, repo, git_buf_cstr(&msg))) < 0)
- goto cleanup;
-
- if ((error = reset_index_and_workdir(
- repo,
- ((flags & GIT_STASH_KEEP_INDEX) != 0) ? i_commit : b_commit,
- (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0,
- (flags & GIT_STASH_INCLUDE_IGNORED) != 0)) < 0)
- goto cleanup;
-
-cleanup:
-
- git_buf_free(&msg);
- git_commit_free(i_commit);
- git_commit_free(b_commit);
- git_commit_free(u_commit);
- git_index_free(index);
-
- return error;
-}
-
-static int retrieve_stash_commit(
- git_commit **commit,
- git_repository *repo,
- size_t index)
-{
- git_reference *stash = NULL;
- git_reflog *reflog = NULL;
- int error;
- size_t max;
- const git_reflog_entry *entry;
-
- if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
- goto cleanup;
-
- if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
- goto cleanup;
-
- max = git_reflog_entrycount(reflog);
- if (!max || index > max - 1) {
- error = GIT_ENOTFOUND;
- giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
- goto cleanup;
- }
-
- entry = git_reflog_entry_byindex(reflog, index);
- if ((error = git_commit_lookup(commit, repo, git_reflog_entry_id_new(entry))) < 0)
- goto cleanup;
-
-cleanup:
- git_reference_free(stash);
- git_reflog_free(reflog);
- return error;
-}
-
-static int retrieve_stash_trees(
- git_tree **out_stash_tree,
- git_tree **out_base_tree,
- git_tree **out_index_tree,
- git_tree **out_index_parent_tree,
- git_tree **out_untracked_tree,
- git_commit *stash_commit)
-{
- git_tree *stash_tree = NULL;
- git_commit *base_commit = NULL;
- git_tree *base_tree = NULL;
- git_commit *index_commit = NULL;
- git_tree *index_tree = NULL;
- git_commit *index_parent_commit = NULL;
- git_tree *index_parent_tree = NULL;
- git_commit *untracked_commit = NULL;
- git_tree *untracked_tree = NULL;
- int error;
-
- if ((error = git_commit_tree(&stash_tree, stash_commit)) < 0)
- goto cleanup;
-
- if ((error = git_commit_parent(&base_commit, stash_commit, 0)) < 0)
- goto cleanup;
- if ((error = git_commit_tree(&base_tree, base_commit)) < 0)
- goto cleanup;
-
- if ((error = git_commit_parent(&index_commit, stash_commit, 1)) < 0)
- goto cleanup;
- if ((error = git_commit_tree(&index_tree, index_commit)) < 0)
- goto cleanup;
-
- if ((error = git_commit_parent(&index_parent_commit, index_commit, 0)) < 0)
- goto cleanup;
- if ((error = git_commit_tree(&index_parent_tree, index_parent_commit)) < 0)
- goto cleanup;
-
- if (git_commit_parentcount(stash_commit) == 3) {
- if ((error = git_commit_parent(&untracked_commit, stash_commit, 2)) < 0)
- goto cleanup;
- if ((error = git_commit_tree(&untracked_tree, untracked_commit)) < 0)
- goto cleanup;
- }
-
- *out_stash_tree = stash_tree;
- *out_base_tree = base_tree;
- *out_index_tree = index_tree;
- *out_index_parent_tree = index_parent_tree;
- *out_untracked_tree = untracked_tree;
-
-cleanup:
- git_commit_free(untracked_commit);
- git_commit_free(index_parent_commit);
- git_commit_free(index_commit);
- git_commit_free(base_commit);
- if (error < 0) {
- git_tree_free(stash_tree);
- git_tree_free(base_tree);
- git_tree_free(index_tree);
- git_tree_free(index_parent_tree);
- git_tree_free(untracked_tree);
- }
- return error;
-}
-
-static int merge_indexes(
- git_index **out,
- git_repository *repo,
- git_tree *ancestor_tree,
- git_index *ours_index,
- git_index *theirs_index)
-{
- git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL;
- git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
- int error;
-
- iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
-
- if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 ||
- (error = git_iterator_for_index(&ours, repo, ours_index, &iter_opts)) < 0 ||
- (error = git_iterator_for_index(&theirs, repo, theirs_index, &iter_opts)) < 0)
- goto done;
-
- error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL);
-
-done:
- git_iterator_free(ancestor);
- git_iterator_free(ours);
- git_iterator_free(theirs);
- return error;
-}
-
-static int merge_index_and_tree(
- git_index **out,
- git_repository *repo,
- git_tree *ancestor_tree,
- git_index *ours_index,
- git_tree *theirs_tree)
-{
- git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL;
- git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
- int error;
-
- iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
-
- if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 ||
- (error = git_iterator_for_index(&ours, repo, ours_index, &iter_opts)) < 0 ||
- (error = git_iterator_for_tree(&theirs, theirs_tree, &iter_opts)) < 0)
- goto done;
-
- error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL);
-
-done:
- git_iterator_free(ancestor);
- git_iterator_free(ours);
- git_iterator_free(theirs);
- return error;
-}
-
-static void normalize_apply_options(
- git_stash_apply_options *opts,
- const git_stash_apply_options *given_apply_opts)
-{
- if (given_apply_opts != NULL) {
- memcpy(opts, given_apply_opts, sizeof(git_stash_apply_options));
- } else {
- git_stash_apply_options default_apply_opts = GIT_STASH_APPLY_OPTIONS_INIT;
- memcpy(opts, &default_apply_opts, sizeof(git_stash_apply_options));
- }
-
- if ((opts->checkout_options.checkout_strategy & (GIT_CHECKOUT_SAFE | GIT_CHECKOUT_FORCE)) == 0)
- opts->checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
-
- if (!opts->checkout_options.our_label)
- opts->checkout_options.our_label = "Updated upstream";
-
- if (!opts->checkout_options.their_label)
- opts->checkout_options.their_label = "Stashed changes";
-}
-
-int git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_stash_apply_options, GIT_STASH_APPLY_OPTIONS_INIT);
- return 0;
-}
-
-#define NOTIFY_PROGRESS(opts, progress_type) \
- do { \
- if ((opts).progress_cb && \
- (error = (opts).progress_cb((progress_type), (opts).progress_payload))) { \
- error = (error < 0) ? error : -1; \
- goto cleanup; \
- } \
- } while(false);
-
-static int ensure_clean_index(git_repository *repo, git_index *index)
-{
- git_tree *head_tree = NULL;
- git_diff *index_diff = NULL;
- int error = 0;
-
- if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
- (error = git_diff_tree_to_index(
- &index_diff, repo, head_tree, index, NULL)) < 0)
- goto done;
-
- if (git_diff_num_deltas(index_diff) > 0) {
- giterr_set(GITERR_STASH, "%" PRIuZ " uncommitted changes exist in the index",
- git_diff_num_deltas(index_diff));
- error = GIT_EUNCOMMITTED;
- }
-
-done:
- git_diff_free(index_diff);
- git_tree_free(head_tree);
- return error;
-}
-
-static int stage_new_file(const git_index_entry **entries, void *data)
-{
- git_index *index = data;
-
- if(entries[0] == NULL)
- return git_index_add(index, entries[1]);
- else
- return git_index_add(index, entries[0]);
-}
-
-static int stage_new_files(
- git_index **out,
- git_tree *parent_tree,
- git_tree *tree)
-{
- git_iterator *iterators[2] = { NULL, NULL };
- git_iterator_options iterator_options = GIT_ITERATOR_OPTIONS_INIT;
- git_index *index = NULL;
- int error;
-
- if ((error = git_index_new(&index)) < 0 ||
- (error = git_iterator_for_tree(
- &iterators[0], parent_tree, &iterator_options)) < 0 ||
- (error = git_iterator_for_tree(
- &iterators[1], tree, &iterator_options)) < 0)
- goto done;
-
- error = git_iterator_walk(iterators, 2, stage_new_file, index);
-
-done:
- if (error < 0)
- git_index_free(index);
- else
- *out = index;
-
- git_iterator_free(iterators[0]);
- git_iterator_free(iterators[1]);
-
- return error;
-}
-
-int git_stash_apply(
- git_repository *repo,
- size_t index,
- const git_stash_apply_options *given_opts)
-{
- git_stash_apply_options opts;
- unsigned int checkout_strategy;
- git_commit *stash_commit = NULL;
- git_tree *stash_tree = NULL;
- git_tree *stash_parent_tree = NULL;
- git_tree *index_tree = NULL;
- git_tree *index_parent_tree = NULL;
- git_tree *untracked_tree = NULL;
- git_index *stash_adds = NULL;
- git_index *repo_index = NULL;
- git_index *unstashed_index = NULL;
- git_index *modified_index = NULL;
- git_index *untracked_index = NULL;
- int error;
-
- GITERR_CHECK_VERSION(given_opts, GIT_STASH_APPLY_OPTIONS_VERSION, "git_stash_apply_options");
-
- normalize_apply_options(&opts, given_opts);
- checkout_strategy = opts.checkout_options.checkout_strategy;
-
- NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_LOADING_STASH);
-
- /* Retrieve commit corresponding to the given stash */
- if ((error = retrieve_stash_commit(&stash_commit, repo, index)) < 0)
- goto cleanup;
-
- /* Retrieve all trees in the stash */
- if ((error = retrieve_stash_trees(
- &stash_tree, &stash_parent_tree, &index_tree,
- &index_parent_tree, &untracked_tree, stash_commit)) < 0)
- goto cleanup;
-
- /* Load repo index */
- if ((error = git_repository_index(&repo_index, repo)) < 0)
- goto cleanup;
-
- NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX);
-
- if ((error = ensure_clean_index(repo, repo_index)) < 0)
- goto cleanup;
-
- /* Restore index if required */
- if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) &&
- git_oid_cmp(git_tree_id(stash_parent_tree), git_tree_id(index_tree))) {
-
- if ((error = merge_index_and_tree(
- &unstashed_index, repo, index_parent_tree, repo_index, index_tree)) < 0)
- goto cleanup;
-
- if (git_index_has_conflicts(unstashed_index)) {
- error = GIT_ECONFLICT;
- goto cleanup;
- }
-
- /* Otherwise, stage any new files in the stash tree. (Note: their
- * previously unstaged contents are staged, not the previously staged.)
- */
- } else if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) == 0) {
- if ((error = stage_new_files(
- &stash_adds, stash_parent_tree, stash_tree)) < 0 ||
- (error = merge_indexes(
- &unstashed_index, repo, stash_parent_tree, repo_index, stash_adds)) < 0)
- goto cleanup;
- }
-
- NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED);
-
- /* Restore modified files in workdir */
- if ((error = merge_index_and_tree(
- &modified_index, repo, stash_parent_tree, repo_index, stash_tree)) < 0)
- goto cleanup;
-
- /* If applicable, restore untracked / ignored files in workdir */
- if (untracked_tree) {
- NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED);
-
- if ((error = merge_index_and_tree(&untracked_index, repo, NULL, repo_index, untracked_tree)) < 0)
- goto cleanup;
- }
-
- if (untracked_index) {
- opts.checkout_options.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
-
- NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED);
-
- if ((error = git_checkout_index(repo, untracked_index, &opts.checkout_options)) < 0)
- goto cleanup;
-
- opts.checkout_options.checkout_strategy = checkout_strategy;
- }
-
-
- /* If there are conflicts in the modified index, then we need to actually
- * check that out as the repo's index. Otherwise, we don't update the
- * index.
- */
-
- if (!git_index_has_conflicts(modified_index))
- opts.checkout_options.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
-
- /* Check out the modified index using the existing repo index as baseline,
- * so that existing modifications in the index can be rewritten even when
- * checking out safely.
- */
- opts.checkout_options.baseline_index = repo_index;
-
- NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED);
-
- if ((error = git_checkout_index(repo, modified_index, &opts.checkout_options)) < 0)
- goto cleanup;
-
- if (unstashed_index && !git_index_has_conflicts(modified_index)) {
- if ((error = git_index_read_index(repo_index, unstashed_index)) < 0)
- goto cleanup;
- }
-
- NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_DONE);
-
- error = git_index_write(repo_index);
-
-cleanup:
- git_index_free(untracked_index);
- git_index_free(modified_index);
- git_index_free(unstashed_index);
- git_index_free(stash_adds);
- git_index_free(repo_index);
- git_tree_free(untracked_tree);
- git_tree_free(index_parent_tree);
- git_tree_free(index_tree);
- git_tree_free(stash_parent_tree);
- git_tree_free(stash_tree);
- git_commit_free(stash_commit);
- return error;
-}
-
-int git_stash_foreach(
- git_repository *repo,
- git_stash_cb callback,
- void *payload)
-{
- git_reference *stash;
- git_reflog *reflog = NULL;
- int error;
- size_t i, max;
- const git_reflog_entry *entry;
-
- error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE);
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- return 0;
- }
- if (error < 0)
- goto cleanup;
-
- if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
- goto cleanup;
-
- max = git_reflog_entrycount(reflog);
- for (i = 0; i < max; i++) {
- entry = git_reflog_entry_byindex(reflog, i);
-
- error = callback(i,
- git_reflog_entry_message(entry),
- git_reflog_entry_id_new(entry),
- payload);
-
- if (error) {
- giterr_set_after_callback(error);
- break;
- }
- }
-
-cleanup:
- git_reference_free(stash);
- git_reflog_free(reflog);
- return error;
-}
-
-int git_stash_drop(
- git_repository *repo,
- size_t index)
-{
- git_transaction *tx;
- git_reference *stash = NULL;
- git_reflog *reflog = NULL;
- size_t max;
- int error;
-
- if ((error = git_transaction_new(&tx, repo)) < 0)
- return error;
-
- if ((error = git_transaction_lock_ref(tx, GIT_REFS_STASH_FILE)) < 0)
- goto cleanup;
-
- if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
- goto cleanup;
-
- if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
- goto cleanup;
-
- max = git_reflog_entrycount(reflog);
-
- if (!max || index > max - 1) {
- error = GIT_ENOTFOUND;
- giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
- goto cleanup;
- }
-
- if ((error = git_reflog_drop(reflog, index, true)) < 0)
- goto cleanup;
-
- if ((error = git_transaction_set_reflog(tx, GIT_REFS_STASH_FILE, reflog)) < 0)
- goto cleanup;
-
- if (max == 1) {
- if ((error = git_transaction_remove(tx, GIT_REFS_STASH_FILE)) < 0)
- goto cleanup;
- } else if (index == 0) {
- const git_reflog_entry *entry;
-
- entry = git_reflog_entry_byindex(reflog, 0);
- if ((error = git_transaction_set_target(tx, GIT_REFS_STASH_FILE, &entry->oid_cur, NULL, NULL)) < 0)
- goto cleanup;
- }
-
- error = git_transaction_commit(tx);
-
-cleanup:
- git_reference_free(stash);
- git_transaction_free(tx);
- git_reflog_free(reflog);
- return error;
-}
-
-int git_stash_pop(
- git_repository *repo,
- size_t index,
- const git_stash_apply_options *options)
-{
- int error;
-
- if ((error = git_stash_apply(repo, index, options)) < 0)
- return error;
-
- return git_stash_drop(repo, index);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "git2.h"
-#include "fileops.h"
-#include "hash.h"
-#include "vector.h"
-#include "tree.h"
-#include "status.h"
-#include "git2/status.h"
-#include "repository.h"
-#include "ignore.h"
-#include "index.h"
-
-#include "git2/diff.h"
-#include "diff.h"
-#include "diff_generate.h"
-
-static unsigned int index_delta2status(const git_diff_delta *head2idx)
-{
- git_status_t st = GIT_STATUS_CURRENT;
-
- switch (head2idx->status) {
- case GIT_DELTA_ADDED:
- case GIT_DELTA_COPIED:
- st = GIT_STATUS_INDEX_NEW;
- break;
- case GIT_DELTA_DELETED:
- st = GIT_STATUS_INDEX_DELETED;
- break;
- case GIT_DELTA_MODIFIED:
- st = GIT_STATUS_INDEX_MODIFIED;
- break;
- case GIT_DELTA_RENAMED:
- st = GIT_STATUS_INDEX_RENAMED;
-
- if (!git_oid_equal(&head2idx->old_file.id, &head2idx->new_file.id))
- st |= GIT_STATUS_INDEX_MODIFIED;
- break;
- case GIT_DELTA_TYPECHANGE:
- st = GIT_STATUS_INDEX_TYPECHANGE;
- break;
- case GIT_DELTA_CONFLICTED:
- st = GIT_STATUS_CONFLICTED;
- break;
- default:
- break;
- }
-
- return st;
-}
-
-static unsigned int workdir_delta2status(
- git_diff *diff, git_diff_delta *idx2wd)
-{
- git_status_t st = GIT_STATUS_CURRENT;
-
- switch (idx2wd->status) {
- case GIT_DELTA_ADDED:
- case GIT_DELTA_COPIED:
- case GIT_DELTA_UNTRACKED:
- st = GIT_STATUS_WT_NEW;
- break;
- case GIT_DELTA_UNREADABLE:
- st = GIT_STATUS_WT_UNREADABLE;
- break;
- case GIT_DELTA_DELETED:
- st = GIT_STATUS_WT_DELETED;
- break;
- case GIT_DELTA_MODIFIED:
- st = GIT_STATUS_WT_MODIFIED;
- break;
- case GIT_DELTA_IGNORED:
- st = GIT_STATUS_IGNORED;
- break;
- case GIT_DELTA_RENAMED:
- st = GIT_STATUS_WT_RENAMED;
-
- if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id)) {
- /* if OIDs don't match, we might need to calculate them now to
- * discern between RENAMED vs RENAMED+MODIFED
- */
- if (git_oid_iszero(&idx2wd->old_file.id) &&
- diff->old_src == GIT_ITERATOR_TYPE_WORKDIR &&
- !git_diff__oid_for_file(
- &idx2wd->old_file.id, diff, idx2wd->old_file.path,
- idx2wd->old_file.mode, idx2wd->old_file.size))
- idx2wd->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
-
- if (git_oid_iszero(&idx2wd->new_file.id) &&
- diff->new_src == GIT_ITERATOR_TYPE_WORKDIR &&
- !git_diff__oid_for_file(
- &idx2wd->new_file.id, diff, idx2wd->new_file.path,
- idx2wd->new_file.mode, idx2wd->new_file.size))
- idx2wd->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
-
- if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id))
- st |= GIT_STATUS_WT_MODIFIED;
- }
- break;
- case GIT_DELTA_TYPECHANGE:
- st = GIT_STATUS_WT_TYPECHANGE;
- break;
- case GIT_DELTA_CONFLICTED:
- st = GIT_STATUS_CONFLICTED;
- break;
- default:
- break;
- }
-
- return st;
-}
-
-static bool status_is_included(
- git_status_list *status,
- git_diff_delta *head2idx,
- git_diff_delta *idx2wd)
-{
- if (!(status->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES))
- return 1;
-
- /* if excluding submodules and this is a submodule everywhere */
- if (head2idx) {
- if (head2idx->status != GIT_DELTA_ADDED &&
- head2idx->old_file.mode != GIT_FILEMODE_COMMIT)
- return 1;
- if (head2idx->status != GIT_DELTA_DELETED &&
- head2idx->new_file.mode != GIT_FILEMODE_COMMIT)
- return 1;
- }
- if (idx2wd) {
- if (idx2wd->status != GIT_DELTA_ADDED &&
- idx2wd->old_file.mode != GIT_FILEMODE_COMMIT)
- return 1;
- if (idx2wd->status != GIT_DELTA_DELETED &&
- idx2wd->new_file.mode != GIT_FILEMODE_COMMIT)
- return 1;
- }
-
- /* only get here if every valid mode is GIT_FILEMODE_COMMIT */
- return 0;
-}
-
-static git_status_t status_compute(
- git_status_list *status,
- git_diff_delta *head2idx,
- git_diff_delta *idx2wd)
-{
- git_status_t st = GIT_STATUS_CURRENT;
-
- if (head2idx)
- st |= index_delta2status(head2idx);
-
- if (idx2wd)
- st |= workdir_delta2status(status->idx2wd, idx2wd);
-
- return st;
-}
-
-static int status_collect(
- git_diff_delta *head2idx,
- git_diff_delta *idx2wd,
- void *payload)
-{
- git_status_list *status = payload;
- git_status_entry *status_entry;
-
- if (!status_is_included(status, head2idx, idx2wd))
- return 0;
-
- status_entry = git__malloc(sizeof(git_status_entry));
- GITERR_CHECK_ALLOC(status_entry);
-
- status_entry->status = status_compute(status, head2idx, idx2wd);
- status_entry->head_to_index = head2idx;
- status_entry->index_to_workdir = idx2wd;
-
- return git_vector_insert(&status->paired, status_entry);
-}
-
-GIT_INLINE(int) status_entry_cmp_base(
- const void *a,
- const void *b,
- int (*strcomp)(const char *a, const char *b))
-{
- const git_status_entry *entry_a = a;
- const git_status_entry *entry_b = b;
- const git_diff_delta *delta_a, *delta_b;
-
- delta_a = entry_a->index_to_workdir ? entry_a->index_to_workdir :
- entry_a->head_to_index;
- delta_b = entry_b->index_to_workdir ? entry_b->index_to_workdir :
- entry_b->head_to_index;
-
- if (!delta_a && delta_b)
- return -1;
- if (delta_a && !delta_b)
- return 1;
- if (!delta_a && !delta_b)
- return 0;
-
- return strcomp(delta_a->new_file.path, delta_b->new_file.path);
-}
-
-static int status_entry_icmp(const void *a, const void *b)
-{
- return status_entry_cmp_base(a, b, git__strcasecmp);
-}
-
-static int status_entry_cmp(const void *a, const void *b)
-{
- return status_entry_cmp_base(a, b, git__strcmp);
-}
-
-static git_status_list *git_status_list_alloc(git_index *index)
-{
- git_status_list *status = NULL;
- int (*entrycmp)(const void *a, const void *b);
-
- if (!(status = git__calloc(1, sizeof(git_status_list))))
- return NULL;
-
- entrycmp = index->ignore_case ? status_entry_icmp : status_entry_cmp;
-
- if (git_vector_init(&status->paired, 0, entrycmp) < 0) {
- git__free(status);
- return NULL;
- }
-
- return status;
-}
-
-static int status_validate_options(const git_status_options *opts)
-{
- if (!opts)
- return 0;
-
- GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
-
- if (opts->show > GIT_STATUS_SHOW_WORKDIR_ONLY) {
- giterr_set(GITERR_INVALID, "Unknown status 'show' option");
- return -1;
- }
-
- if ((opts->flags & GIT_STATUS_OPT_NO_REFRESH) != 0 &&
- (opts->flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) {
- giterr_set(GITERR_INVALID, "Updating index from status "
- "is not allowed when index refresh is disabled");
- return -1;
- }
-
- return 0;
-}
-
-int git_status_list_new(
- git_status_list **out,
- git_repository *repo,
- const git_status_options *opts)
-{
- git_index *index = NULL;
- git_status_list *status = NULL;
- git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options findopt = GIT_DIFF_FIND_OPTIONS_INIT;
- git_tree *head = NULL;
- git_status_show_t show =
- opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
- int error = 0;
- unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS;
-
- *out = NULL;
-
- if (status_validate_options(opts) < 0)
- return -1;
-
- if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 ||
- (error = git_repository_index(&index, repo)) < 0)
- return error;
-
- /* if there is no HEAD, that's okay - we'll make an empty iterator */
- if ((error = git_repository_head_tree(&head, repo)) < 0) {
- if (error != GIT_ENOTFOUND && error != GIT_EUNBORNBRANCH)
- goto done;
- giterr_clear();
- }
-
- /* refresh index from disk unless prevented */
- if ((flags & GIT_STATUS_OPT_NO_REFRESH) == 0 &&
- git_index_read(index, false) < 0)
- giterr_clear();
-
- status = git_status_list_alloc(index);
- GITERR_CHECK_ALLOC(status);
-
- if (opts) {
- memcpy(&status->opts, opts, sizeof(git_status_options));
- memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
- }
-
- diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
- findopt.flags = GIT_DIFF_FIND_FOR_UNTRACKED;
-
- if ((flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
- diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
- if ((flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
- diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;
- if ((flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)
- diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
- if ((flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
- diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
- if ((flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0)
- diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH;
- if ((flags & GIT_STATUS_OPT_RECURSE_IGNORED_DIRS) != 0)
- diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS;
- if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
- diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
- if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0)
- diffopt.flags = diffopt.flags | GIT_DIFF_UPDATE_INDEX;
- if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE) != 0)
- diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE;
- if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED) != 0)
- diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED;
-
- if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
- findopt.flags = findopt.flags |
- GIT_DIFF_FIND_AND_BREAK_REWRITES |
- GIT_DIFF_FIND_RENAMES_FROM_REWRITES |
- GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY;
-
- if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
- if ((error = git_diff_tree_to_index(
- &status->head2idx, repo, head, index, &diffopt)) < 0)
- goto done;
-
- if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 &&
- (error = git_diff_find_similar(status->head2idx, &findopt)) < 0)
- goto done;
- }
-
- if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
- if ((error = git_diff_index_to_workdir(
- &status->idx2wd, repo, index, &diffopt)) < 0) {
- goto done;
- }
-
- if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
- (error = git_diff_find_similar(status->idx2wd, &findopt)) < 0)
- goto done;
- }
-
- error = git_diff__paired_foreach(
- status->head2idx, status->idx2wd, status_collect, status);
- if (error < 0)
- goto done;
-
- if (flags & GIT_STATUS_OPT_SORT_CASE_SENSITIVELY)
- git_vector_set_cmp(&status->paired, status_entry_cmp);
- if (flags & GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY)
- git_vector_set_cmp(&status->paired, status_entry_icmp);
-
- if ((flags &
- (GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
- GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR |
- GIT_STATUS_OPT_SORT_CASE_SENSITIVELY |
- GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY)) != 0)
- git_vector_sort(&status->paired);
-
-done:
- if (error < 0) {
- git_status_list_free(status);
- status = NULL;
- }
-
- *out = status;
-
- git_tree_free(head);
- git_index_free(index);
-
- return error;
-}
-
-size_t git_status_list_entrycount(git_status_list *status)
-{
- assert(status);
-
- return status->paired.length;
-}
-
-const git_status_entry *git_status_byindex(git_status_list *status, size_t i)
-{
- assert(status);
-
- return git_vector_get(&status->paired, i);
-}
-
-void git_status_list_free(git_status_list *status)
-{
- if (status == NULL)
- return;
-
- git_diff_free(status->head2idx);
- git_diff_free(status->idx2wd);
-
- git_vector_free_deep(&status->paired);
-
- git__memzero(status, sizeof(*status));
- git__free(status);
-}
-
-int git_status_foreach_ext(
- git_repository *repo,
- const git_status_options *opts,
- git_status_cb cb,
- void *payload)
-{
- git_status_list *status;
- const git_status_entry *status_entry;
- size_t i;
- int error = 0;
-
- if ((error = git_status_list_new(&status, repo, opts)) < 0) {
- return error;
- }
-
- git_vector_foreach(&status->paired, i, status_entry) {
- const char *path = status_entry->head_to_index ?
- status_entry->head_to_index->old_file.path :
- status_entry->index_to_workdir->old_file.path;
-
- if ((error = cb(path, status_entry->status, payload)) != 0) {
- giterr_set_after_callback(error);
- break;
- }
- }
-
- git_status_list_free(status);
-
- return error;
-}
-
-int git_status_foreach(git_repository *repo, git_status_cb cb, void *payload)
-{
- return git_status_foreach_ext(repo, NULL, cb, payload);
-}
-
-struct status_file_info {
- char *expected;
- unsigned int count;
- unsigned int status;
- int fnm_flags;
- int ambiguous;
-};
-
-static int get_one_status(const char *path, unsigned int status, void *data)
-{
- struct status_file_info *sfi = data;
- int (*strcomp)(const char *a, const char *b);
-
- sfi->count++;
- sfi->status = status;
-
- strcomp = (sfi->fnm_flags & FNM_CASEFOLD) ? git__strcasecmp : git__strcmp;
-
- if (sfi->count > 1 ||
- (strcomp(sfi->expected, path) != 0 &&
- p_fnmatch(sfi->expected, path, sfi->fnm_flags) != 0))
- {
- sfi->ambiguous = true;
- return GIT_EAMBIGUOUS; /* giterr_set will be done by caller */
- }
-
- return 0;
-}
-
-int git_status_file(
- unsigned int *status_flags,
- git_repository *repo,
- const char *path)
-{
- int error;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- struct status_file_info sfi = {0};
- git_index *index;
-
- assert(status_flags && repo && path);
-
- if ((error = git_repository_index__weakptr(&index, repo)) < 0)
- return error;
-
- if ((sfi.expected = git__strdup(path)) == NULL)
- return -1;
- if (index->ignore_case)
- sfi.fnm_flags = FNM_CASEFOLD;
-
- opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
- opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
- GIT_STATUS_OPT_RECURSE_IGNORED_DIRS |
- GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
- GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
- GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
- opts.pathspec.count = 1;
- opts.pathspec.strings = &sfi.expected;
-
- error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
-
- if (error < 0 && sfi.ambiguous) {
- giterr_set(GITERR_INVALID,
- "Ambiguous path '%s' given to git_status_file", sfi.expected);
- error = GIT_EAMBIGUOUS;
- }
-
- if (!error && !sfi.count) {
- giterr_set(GITERR_INVALID,
- "Attempt to get status of nonexistent file '%s'", path);
- error = GIT_ENOTFOUND;
- }
-
- *status_flags = sfi.status;
-
- git__free(sfi.expected);
-
- return error;
-}
-
-int git_status_should_ignore(
- int *ignored,
- git_repository *repo,
- const char *path)
-{
- return git_ignore_path_is_ignored(ignored, repo, path);
-}
-
-int git_status_init_options(git_status_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_status_options, GIT_STATUS_OPTIONS_INIT);
- return 0;
-}
-
-int git_status_list_get_perfdata(
- git_diff_perfdata *out, const git_status_list *status)
-{
- assert(out);
- GITERR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
-
- out->stat_calls = 0;
- out->oid_calculations = 0;
-
- if (status->head2idx) {
- out->stat_calls += status->head2idx->perf.stat_calls;
- out->oid_calculations += status->head2idx->perf.oid_calculations;
- }
- if (status->idx2wd) {
- out->stat_calls += status->idx2wd->perf.stat_calls;
- out->oid_calculations += status->idx2wd->perf.oid_calculations;
- }
-
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_status_h__
-#define INCLUDE_status_h__
-
-#include "diff.h"
-#include "git2/status.h"
-#include "git2/diff.h"
-
-struct git_status_list {
- git_status_options opts;
-
- git_diff *head2idx;
- git_diff *idx2wd;
-
- git_vector paired;
-};
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifdef GIT_SECURE_TRANSPORT
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/SecureTransport.h>
-#include <Security/SecCertificate.h>
-
-#include "git2/transport.h"
-
-#include "socket_stream.h"
-#include "curl_stream.h"
-
-static int stransport_error(OSStatus ret)
-{
- CFStringRef message;
-
- if (ret == noErr || ret == errSSLClosedGraceful) {
- giterr_clear();
- return 0;
- }
-
-#if !TARGET_OS_IPHONE
- message = SecCopyErrorMessageString(ret, NULL);
- GITERR_CHECK_ALLOC(message);
-
- giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8));
- CFRelease(message);
-#else
- giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret);
- GIT_UNUSED(message);
-#endif
-
- return -1;
-}
-
-typedef struct {
- git_stream parent;
- git_stream *io;
- SSLContextRef ctx;
- CFDataRef der_data;
- git_cert_x509 cert_info;
-} stransport_stream;
-
-static int stransport_connect(git_stream *stream)
-{
- stransport_stream *st = (stransport_stream *) stream;
- int error;
- SecTrustRef trust = NULL;
- SecTrustResultType sec_res;
- OSStatus ret;
-
- if ((error = git_stream_connect(st->io)) < 0)
- return error;
-
- ret = SSLHandshake(st->ctx);
- if (ret != errSSLServerAuthCompleted) {
- giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", ret);
- return -1;
- }
-
- if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
- goto on_error;
-
- if (!trust)
- return GIT_ECERTIFICATE;
-
- if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
- goto on_error;
-
- CFRelease(trust);
-
- if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) {
- giterr_set(GITERR_SSL, "internal security trust error");
- return -1;
- }
-
- if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
- sec_res == kSecTrustResultFatalTrustFailure)
- return GIT_ECERTIFICATE;
-
- return 0;
-
-on_error:
- if (trust)
- CFRelease(trust);
-
- return stransport_error(ret);
-}
-
-static int stransport_certificate(git_cert **out, git_stream *stream)
-{
- stransport_stream *st = (stransport_stream *) stream;
- SecTrustRef trust = NULL;
- SecCertificateRef sec_cert;
- OSStatus ret;
-
- if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
- return stransport_error(ret);
-
- sec_cert = SecTrustGetCertificateAtIndex(trust, 0);
- st->der_data = SecCertificateCopyData(sec_cert);
- CFRelease(trust);
-
- if (st->der_data == NULL) {
- giterr_set(GITERR_SSL, "retrieved invalid certificate data");
- return -1;
- }
-
- st->cert_info.parent.cert_type = GIT_CERT_X509;
- st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
- st->cert_info.len = CFDataGetLength(st->der_data);
-
- *out = (git_cert *)&st->cert_info;
- return 0;
-}
-
-static int stransport_set_proxy(
- git_stream *stream,
- const git_proxy_options *proxy_opts)
-{
- stransport_stream *st = (stransport_stream *) stream;
-
- return git_stream_set_proxy(st->io, proxy_opts);
-}
-
-/*
- * Contrary to typical network IO callbacks, Secure Transport write callback is
- * expected to write *all* passed data, not just as much as it can, and any
- * other case would be considered a failure.
- *
- * This behavior is actually not specified in the Apple documentation, but is
- * required for things to work correctly (and incidentally, that's also how
- * Apple implements it in its projects at opensource.apple.com).
- *
- * Libgit2 streams happen to already have this very behavior so this is just
- * passthrough.
- */
-static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
-{
- git_stream *io = (git_stream *) conn;
-
- if (git_stream_write(io, data, *len, 0) < 0) {
- return -36; /* "ioErr" from MacErrors.h which is not available on iOS */
- }
-
- return noErr;
-}
-
-static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags)
-{
- stransport_stream *st = (stransport_stream *) stream;
- size_t data_len, processed;
- OSStatus ret;
-
- GIT_UNUSED(flags);
-
- data_len = len;
- if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
- return stransport_error(ret);
-
- return processed;
-}
-
-/*
- * Contrary to typical network IO callbacks, Secure Transport read callback is
- * expected to read *exactly* the requested number of bytes, not just as much
- * as it can, and any other case would be considered a failure.
- *
- * This behavior is actually not specified in the Apple documentation, but is
- * required for things to work correctly (and incidentally, that's also how
- * Apple implements it in its projects at opensource.apple.com).
- */
-static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
-{
- git_stream *io = (git_stream *) conn;
- OSStatus error = noErr;
- size_t off = 0;
- ssize_t ret;
-
- do {
- ret = git_stream_read(io, data + off, *len - off);
- if (ret < 0) {
- error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */
- break;
- }
- if (ret == 0) {
- error = errSSLClosedGraceful;
- break;
- }
-
- off += ret;
- } while (off < *len);
-
- *len = off;
- return error;
-}
-
-static ssize_t stransport_read(git_stream *stream, void *data, size_t len)
-{
- stransport_stream *st = (stransport_stream *) stream;
- size_t processed;
- OSStatus ret;
-
- if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr)
- return stransport_error(ret);
-
- return processed;
-}
-
-static int stransport_close(git_stream *stream)
-{
- stransport_stream *st = (stransport_stream *) stream;
- OSStatus ret;
-
- ret = SSLClose(st->ctx);
- if (ret != noErr && ret != errSSLClosedGraceful)
- return stransport_error(ret);
-
- return git_stream_close(st->io);
-}
-
-static void stransport_free(git_stream *stream)
-{
- stransport_stream *st = (stransport_stream *) stream;
-
- git_stream_free(st->io);
- CFRelease(st->ctx);
- if (st->der_data)
- CFRelease(st->der_data);
- git__free(st);
-}
-
-int git_stransport_stream_new(git_stream **out, const char *host, const char *port)
-{
- stransport_stream *st;
- int error;
- OSStatus ret;
-
- assert(out && host);
-
- st = git__calloc(1, sizeof(stransport_stream));
- GITERR_CHECK_ALLOC(st);
-
-#ifdef GIT_CURL
- error = git_curl_stream_new(&st->io, host, port);
-#else
- error = git_socket_stream_new(&st->io, host, port);
-#endif
-
- if (error < 0){
- git__free(st);
- return error;
- }
-
- st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
- if (!st->ctx) {
- giterr_set(GITERR_NET, "failed to create SSL context");
- git__free(st);
- return -1;
- }
-
- if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
- (ret = SSLSetConnection(st->ctx, st->io)) != noErr ||
- (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
- (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
- (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
- (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
- CFRelease(st->ctx);
- git__free(st);
- return stransport_error(ret);
- }
-
- st->parent.version = GIT_STREAM_VERSION;
- st->parent.encrypted = 1;
- st->parent.proxy_support = git_stream_supports_proxy(st->io);
- st->parent.connect = stransport_connect;
- st->parent.certificate = stransport_certificate;
- st->parent.set_proxy = stransport_set_proxy;
- st->parent.read = stransport_read;
- st->parent.write = stransport_write;
- st->parent.close = stransport_close;
- st->parent.free = stransport_free;
-
- *out = (git_stream *) st;
- return 0;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_stransport_stream_h__
-#define INCLUDE_stransport_stream_h__
-
-#include "git2/sys/stream.h"
-
-extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_stream_h__
-#define INCLUDE_stream_h__
-
-#include "common.h"
-#include "git2/sys/stream.h"
-
-GIT_INLINE(int) git_stream_connect(git_stream *st)
-{
- return st->connect(st);
-}
-
-GIT_INLINE(int) git_stream_is_encrypted(git_stream *st)
-{
- return st->encrypted;
-}
-
-GIT_INLINE(int) git_stream_certificate(git_cert **out, git_stream *st)
-{
- if (!st->encrypted) {
- giterr_set(GITERR_INVALID, "an unencrypted stream does not have a certificate");
- return -1;
- }
-
- return st->certificate(out, st);
-}
-
-GIT_INLINE(int) git_stream_supports_proxy(git_stream *st)
-{
- return st->proxy_support;
-}
-
-GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const git_proxy_options *proxy_opts)
-{
- if (!st->proxy_support) {
- giterr_set(GITERR_INVALID, "proxy not supported on this stream");
- return -1;
- }
-
- return st->set_proxy(st, proxy_opts);
-}
-
-GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len)
-{
- return st->read(st, data, len);
-}
-
-GIT_INLINE(ssize_t) git_stream_write(git_stream *st, const char *data, size_t len, int flags)
-{
- return st->write(st, data, len, flags);
-}
-
-GIT_INLINE(int) git_stream_close(git_stream *st)
-{
- return st->close(st);
-}
-
-GIT_INLINE(void) git_stream_free(git_stream *st)
-{
- if (!st)
- return;
-
- st->free(st);
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "strmap.h"
-
-int git_strmap_next(
- void **data,
- git_strmap_iter* iter,
- git_strmap *map)
-{
- if (!map)
- return GIT_ERROR;
-
- while (*iter != git_strmap_end(map)) {
- if (!(git_strmap_has_data(map, *iter))) {
- ++(*iter);
- continue;
- }
-
- *data = git_strmap_value_at(map, *iter);
-
- ++(*iter);
-
- return GIT_OK;
- }
-
- return GIT_ITEROVER;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_strmap_h__
-#define INCLUDE_strmap_h__
-
-#include "common.h"
-
-#define kmalloc git__malloc
-#define kcalloc git__calloc
-#define krealloc git__realloc
-#define kreallocarray git__reallocarray
-#define kfree git__free
-#include "khash.h"
-
-__KHASH_TYPE(str, const char *, void *)
-typedef khash_t(str) git_strmap;
-typedef khiter_t git_strmap_iter;
-
-#define GIT__USE_STRMAP \
- __KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
-
-#define git_strmap_alloc(hp) \
- ((*(hp) = kh_init(str)) == NULL) ? giterr_set_oom(), -1 : 0
-
-#define git_strmap_free(h) kh_destroy(str, h), h = NULL
-#define git_strmap_clear(h) kh_clear(str, h)
-
-#define git_strmap_num_entries(h) kh_size(h)
-
-#define git_strmap_lookup_index(h, k) kh_get(str, h, k)
-#define git_strmap_valid_index(h, idx) (idx != kh_end(h))
-
-#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h))
-#define git_strmap_has_data(h, idx) kh_exist(h, idx)
-
-#define git_strmap_key(h, idx) kh_key(h, idx)
-#define git_strmap_value_at(h, idx) kh_val(h, idx)
-#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v
-#define git_strmap_delete_at(h, idx) kh_del(str, h, idx)
-
-#define git_strmap_insert(h, key, val, rval) do { \
- khiter_t __pos = kh_put(str, h, key, &rval); \
- if (rval >= 0) { \
- if (rval == 0) kh_key(h, __pos) = key; \
- kh_val(h, __pos) = val; \
- } } while (0)
-
-#define git_strmap_insert2(h, key, val, oldv, rval) do { \
- khiter_t __pos = kh_put(str, h, key, &rval); \
- if (rval >= 0) { \
- if (rval == 0) { \
- oldv = kh_val(h, __pos); \
- kh_key(h, __pos) = key; \
- } else { oldv = NULL; } \
- kh_val(h, __pos) = val; \
- } } while (0)
-
-#define git_strmap_delete(h, key) do { \
- khiter_t __pos = git_strmap_lookup_index(h, key); \
- if (git_strmap_valid_index(h, __pos)) \
- git_strmap_delete_at(h, __pos); } while (0)
-
-#define git_strmap_foreach kh_foreach
-#define git_strmap_foreach_value kh_foreach_value
-
-#define git_strmap_begin kh_begin
-#define git_strmap_end kh_end
-
-int git_strmap_next(
- void **data,
- git_strmap_iter* iter,
- git_strmap *map);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_strlen_h__
-#define INCLUDE_strlen_h__
-
-#if defined(__MINGW32__) || defined(__sun) || defined(__APPLE__) || defined(__MidnightBSD__) ||\
- (defined(_MSC_VER) && _MSC_VER < 1500)
-# define NO_STRNLEN
-#endif
-
-#ifdef NO_STRNLEN
-GIT_INLINE(size_t) p_strnlen(const char *s, size_t maxlen) {
- const char *end = memchr(s, 0, maxlen);
- return end ? (size_t)(end - s) : maxlen;
-}
-#else
-# define p_strnlen strnlen
-#endif
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "git2/config.h"
-#include "git2/sys/config.h"
-#include "git2/types.h"
-#include "git2/index.h"
-#include "buffer.h"
-#include "buf_text.h"
-#include "vector.h"
-#include "posix.h"
-#include "config_file.h"
-#include "config.h"
-#include "repository.h"
-#include "submodule.h"
-#include "tree.h"
-#include "iterator.h"
-#include "path.h"
-#include "index.h"
-
-#define GIT_MODULES_FILE ".gitmodules"
-
-static git_cvar_map _sm_update_map[] = {
- {GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
- {GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
- {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
- {GIT_CVAR_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
- {GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_UPDATE_NONE},
- {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_UPDATE_CHECKOUT},
-};
-
-static git_cvar_map _sm_ignore_map[] = {
- {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE},
- {GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
- {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
- {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
- {GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_IGNORE_NONE},
- {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_IGNORE_ALL},
-};
-
-static git_cvar_map _sm_recurse_map[] = {
- {GIT_CVAR_STRING, "on-demand", GIT_SUBMODULE_RECURSE_ONDEMAND},
- {GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_RECURSE_NO},
- {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_RECURSE_YES},
-};
-
-enum {
- CACHE_OK = 0,
- CACHE_REFRESH = 1,
- CACHE_FLUSH = 2
-};
-enum {
- GITMODULES_EXISTING = 0,
- GITMODULES_CREATE = 1,
-};
-
-static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
-{
- khint_t h;
-
- for (h = 0; *s; ++s)
- if (s[1] != '\0' || *s != '/')
- h = (h << 5) - h + *s;
-
- return h;
-}
-
-static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
-{
- size_t alen = a ? strlen(a) : 0;
- size_t blen = b ? strlen(b) : 0;
-
- if (alen > 0 && a[alen - 1] == '/')
- alen--;
- if (blen > 0 && b[blen - 1] == '/')
- blen--;
-
- return (alen == 0 && blen == 0) ||
- (alen == blen && strncmp(a, b, alen) == 0);
-}
-
-__KHASH_IMPL(
- str, static kh_inline, const char *, void *, 1,
- str_hash_no_trailing_slash, str_equal_no_trailing_slash)
-
-static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name);
-static git_config_backend *open_gitmodules(git_repository *repo, int gitmod);
-static git_config *gitmodules_snapshot(git_repository *repo);
-static int get_url_base(git_buf *url, git_repository *repo);
-static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo);
-static int lookup_default_remote(git_remote **remote, git_repository *repo);
-static int submodule_load_each(const git_config_entry *entry, void *payload);
-static int submodule_read_config(git_submodule *sm, git_config *cfg);
-static int submodule_load_from_wd_lite(git_submodule *);
-static void submodule_get_index_status(unsigned int *, git_submodule *);
-static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t);
-static void submodule_update_from_index_entry(git_submodule *sm, const git_index_entry *ie);
-static void submodule_update_from_head_data(git_submodule *sm, mode_t mode, const git_oid *id);
-
-static int submodule_cmp(const void *a, const void *b)
-{
- return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
-}
-
-static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
-{
- ssize_t idx = git_buf_rfind(key, '.');
- git_buf_truncate(key, (size_t)(idx + 1));
- return git_buf_puts(key, suffix);
-}
-
-/*
- * PUBLIC APIS
- */
-
-static void submodule_set_lookup_error(int error, const char *name)
-{
- if (!error)
- return;
-
- giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ?
- "No submodule named '%s'" :
- "Submodule '%s' has not been added yet", name);
-}
-
-typedef struct {
- const char *path;
- char *name;
-} fbp_data;
-
-static int find_by_path(const git_config_entry *entry, void *payload)
-{
- fbp_data *data = payload;
-
- if (!strcmp(entry->value, data->path)) {
- const char *fdot, *ldot;
- fdot = strchr(entry->name, '.');
- ldot = strrchr(entry->name, '.');
- data->name = git__strndup(fdot + 1, ldot - fdot - 1);
- GITERR_CHECK_ALLOC(data->name);
- }
-
- return 0;
-}
-
-/**
- * Find out the name of a submodule from its path
- */
-static int name_from_path(git_buf *out, git_config *cfg, const char *path)
-{
- const char *key = "submodule\\..*\\.path";
- git_config_iterator *iter;
- git_config_entry *entry;
- int error;
-
- if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0)
- return error;
-
- while ((error = git_config_next(&entry, iter)) == 0) {
- const char *fdot, *ldot;
- /* TODO: this should maybe be strcasecmp on a case-insensitive fs */
- if (strcmp(path, entry->value) != 0)
- continue;
-
- fdot = strchr(entry->name, '.');
- ldot = strrchr(entry->name, '.');
-
- git_buf_clear(out);
- git_buf_put(out, fdot + 1, ldot - fdot - 1);
- goto cleanup;
- }
-
- if (error == GIT_ITEROVER) {
- giterr_set(GITERR_SUBMODULE, "could not find a submodule name for '%s'", path);
- error = GIT_ENOTFOUND;
- }
-
-cleanup:
- git_config_iterator_free(iter);
- return error;
-}
-
-int git_submodule_lookup(
- git_submodule **out, /* NULL if user only wants to test existence */
- git_repository *repo,
- const char *name) /* trailing slash is allowed */
-{
- int error;
- unsigned int location;
- git_submodule *sm;
-
- assert(repo && name);
-
- if ((error = submodule_alloc(&sm, repo, name)) < 0)
- return error;
-
- if ((error = git_submodule_reload(sm, false)) < 0) {
- git_submodule_free(sm);
- return error;
- }
-
- if ((error = git_submodule_location(&location, sm)) < 0) {
- git_submodule_free(sm);
- return error;
- }
-
- /* If it's not configured or we're looking by path */
- if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
- git_config_backend *mods;
- const char *pattern = "submodule\\..*\\.path";
- git_buf path = GIT_BUF_INIT;
- fbp_data data = { NULL, NULL };
-
- git_buf_puts(&path, name);
- while (path.ptr[path.size-1] == '/') {
- path.ptr[--path.size] = '\0';
- }
- data.path = path.ptr;
-
- mods = open_gitmodules(repo, GITMODULES_EXISTING);
-
- if (mods)
- error = git_config_file_foreach_match(mods, pattern, find_by_path, &data);
-
- git_config_file_free(mods);
-
- if (error < 0) {
- git_submodule_free(sm);
- git_buf_free(&path);
- return error;
- }
-
- if (data.name) {
- git__free(sm->name);
- sm->name = data.name;
- sm->path = git_buf_detach(&path);
-
- /* Try to load again with the right name */
- if ((error = git_submodule_reload(sm, false)) < 0) {
- git_submodule_free(sm);
- return error;
- }
- }
-
- git_buf_free(&path);
- }
-
- if ((error = git_submodule_location(&location, sm)) < 0) {
- git_submodule_free(sm);
- return error;
- }
-
- /* If we still haven't found it, do the WD check */
- if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
- git_submodule_free(sm);
- error = GIT_ENOTFOUND;
-
- /* If it's not configured, we still check if there's a repo at the path */
- if (git_repository_workdir(repo)) {
- git_buf path = GIT_BUF_INIT;
- if (git_buf_join3(&path,
- '/', git_repository_workdir(repo), name, DOT_GIT) < 0)
- return -1;
-
- if (git_path_exists(path.ptr))
- error = GIT_EEXISTS;
-
- git_buf_free(&path);
- }
-
- submodule_set_lookup_error(error, name);
- return error;
- }
-
- if (out)
- *out = sm;
- else
- git_submodule_free(sm);
-
- return 0;
-}
-
-static void submodule_free_dup(void *sm)
-{
- git_submodule_free(sm);
-}
-
-static int submodule_get_or_create(git_submodule **out, git_repository *repo, git_strmap *map, const char *name)
-{
- int error = 0;
- khiter_t pos;
- git_submodule *sm = NULL;
-
- pos = git_strmap_lookup_index(map, name);
- if (git_strmap_valid_index(map, pos)) {
- sm = git_strmap_value_at(map, pos);
- goto done;
- }
-
- /* if the submodule doesn't exist yet in the map, create it */
- if ((error = submodule_alloc(&sm, repo, name)) < 0)
- return error;
-
- pos = kh_put(str, map, sm->name, &error);
- /* nobody can beat us to adding it */
- assert(error != 0);
- if (error < 0) {
- git_submodule_free(sm);
- return error;
- }
-
- git_strmap_set_value_at(map, pos, sm);
-
-done:
- GIT_REFCOUNT_INC(sm);
- *out = sm;
- return 0;
-}
-
-static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg)
-{
- int error;
- git_iterator *i;
- const git_index_entry *entry;
- git_buf name = GIT_BUF_INIT;
-
- if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
- return error;
-
- while (!(error = git_iterator_advance(&entry, i))) {
- khiter_t pos = git_strmap_lookup_index(map, entry->path);
- git_submodule *sm;
-
- git_buf_clear(&name);
- if (!name_from_path(&name, cfg, entry->path)) {
- git_strmap_lookup_index(map, name.ptr);
- }
-
- if (git_strmap_valid_index(map, pos)) {
- sm = git_strmap_value_at(map, pos);
-
- if (S_ISGITLINK(entry->mode))
- submodule_update_from_index_entry(sm, entry);
- else
- sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
- } else if (S_ISGITLINK(entry->mode)) {
- if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name.ptr ? name.ptr : entry->path)) {
- submodule_update_from_index_entry(sm, entry);
- git_submodule_free(sm);
- }
- }
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- git_buf_free(&name);
- git_iterator_free(i);
-
- return error;
-}
-
-static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg)
-{
- int error;
- git_iterator *i;
- const git_index_entry *entry;
- git_buf name = GIT_BUF_INIT;
-
- if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
- return error;
-
- while (!(error = git_iterator_advance(&entry, i))) {
- khiter_t pos = git_strmap_lookup_index(map, entry->path);
- git_submodule *sm;
-
- git_buf_clear(&name);
- if (!name_from_path(&name, cfg, entry->path)) {
- git_strmap_lookup_index(map, name.ptr);
- }
-
- if (git_strmap_valid_index(map, pos)) {
- sm = git_strmap_value_at(map, pos);
-
- if (S_ISGITLINK(entry->mode))
- submodule_update_from_head_data(sm, entry->mode, &entry->id);
- else
- sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
- } else if (S_ISGITLINK(entry->mode)) {
- if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name.ptr ? name.ptr : entry->path)) {
- submodule_update_from_head_data(
- sm, entry->mode, &entry->id);
- git_submodule_free(sm);
- }
- }
- }
-
- if (error == GIT_ITEROVER)
- error = 0;
-
- git_buf_free(&name);
- git_iterator_free(i);
-
- return error;
-}
-
-/* If have_sm is true, sm is populated, otherwise map an repo are. */
-typedef struct {
- git_config *mods;
- git_strmap *map;
- git_repository *repo;
-} lfc_data;
-
-static int all_submodules(git_repository *repo, git_strmap *map)
-{
- int error = 0;
- git_index *idx = NULL;
- git_tree *head = NULL;
- const char *wd = NULL;
- git_buf path = GIT_BUF_INIT;
- git_submodule *sm;
- git_config *mods = NULL;
- uint32_t mask;
-
- assert(repo && map);
-
- /* get sources that we will need to check */
- if (git_repository_index(&idx, repo) < 0)
- giterr_clear();
- if (git_repository_head_tree(&head, repo) < 0)
- giterr_clear();
-
- wd = git_repository_workdir(repo);
- if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0)
- goto cleanup;
-
- /* clear submodule flags that are to be refreshed */
- mask = 0;
- mask |= GIT_SUBMODULE_STATUS_IN_INDEX |
- GIT_SUBMODULE_STATUS__INDEX_FLAGS |
- GIT_SUBMODULE_STATUS__INDEX_OID_VALID |
- GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
-
- mask |= GIT_SUBMODULE_STATUS_IN_HEAD |
- GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
- mask |= GIT_SUBMODULE_STATUS_IN_CONFIG;
- if (mask != 0)
- mask |= GIT_SUBMODULE_STATUS_IN_WD |
- GIT_SUBMODULE_STATUS__WD_SCANNED |
- GIT_SUBMODULE_STATUS__WD_FLAGS |
- GIT_SUBMODULE_STATUS__WD_OID_VALID;
-
- /* add submodule information from .gitmodules */
- if (wd) {
- lfc_data data = { 0 };
- data.map = map;
- data.repo = repo;
-
- if ((mods = gitmodules_snapshot(repo)) == NULL)
- goto cleanup;
-
- data.mods = mods;
- if ((error = git_config_foreach(
- mods, submodule_load_each, &data)) < 0)
- goto cleanup;
- }
- /* add back submodule information from index */
- if (idx) {
- if ((error = submodules_from_index(map, idx, mods)) < 0)
- goto cleanup;
- }
- /* add submodule information from HEAD */
- if (head) {
- if ((error = submodules_from_head(map, head, mods)) < 0)
- goto cleanup;
- }
- /* shallow scan submodules in work tree as needed */
- if (wd && mask != 0) {
- git_strmap_foreach_value(map, sm, {
- submodule_load_from_wd_lite(sm);
- });
- }
-
-cleanup:
- git_config_free(mods);
- /* TODO: if we got an error, mark submodule config as invalid? */
- git_index_free(idx);
- git_tree_free(head);
- git_buf_free(&path);
- return error;
-}
-
-int git_submodule_foreach(
- git_repository *repo,
- git_submodule_cb callback,
- void *payload)
-{
- git_vector snapshot = GIT_VECTOR_INIT;
- git_strmap *submodules;
- git_submodule *sm;
- int error;
- size_t i;
-
- if ((error = git_strmap_alloc(&submodules)) < 0)
- return error;
-
- if ((error = all_submodules(repo, submodules)) < 0)
- goto done;
-
- if (!(error = git_vector_init(
- &snapshot, kh_size(submodules), submodule_cmp))) {
-
- git_strmap_foreach_value(submodules, sm, {
- if ((error = git_vector_insert(&snapshot, sm)) < 0)
- break;
- GIT_REFCOUNT_INC(sm);
- });
- }
-
- if (error < 0)
- goto done;
-
- git_vector_uniq(&snapshot, submodule_free_dup);
-
- git_vector_foreach(&snapshot, i, sm) {
- if ((error = callback(sm, sm->name, payload)) != 0) {
- giterr_set_after_callback(error);
- break;
- }
- }
-
-done:
- git_vector_foreach(&snapshot, i, sm)
- git_submodule_free(sm);
- git_vector_free(&snapshot);
-
- git_strmap_foreach_value(submodules, sm, {
- git_submodule_free(sm);
- });
- git_strmap_free(submodules);
-
- return error;
-}
-
-static int submodule_repo_init(
- git_repository **out,
- git_repository *parent_repo,
- const char *path,
- const char *url,
- bool use_gitlink)
-{
- int error = 0;
- git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
- git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
- git_repository *subrepo = NULL;
-
- error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
- if (error < 0)
- goto cleanup;
-
- initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT;
- initopt.origin_url = url;
-
- /* init submodule repository and add origin remote as needed */
-
- /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
- * gitlink in the sub-repo workdir directory to that repository
- *
- * Old style: sub-repo goes directly into repo/<name>/.git/
- */
- if (use_gitlink) {
- error = git_buf_join3(
- &repodir, '/', git_repository_path(parent_repo), "modules", path);
- if (error < 0)
- goto cleanup;
-
- initopt.workdir_path = workdir.ptr;
- initopt.flags |=
- GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
- GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
-
- error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
- } else
- error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt);
-
-cleanup:
- git_buf_free(&workdir);
- git_buf_free(&repodir);
-
- *out = subrepo;
-
- return error;
-}
-
-int git_submodule_add_setup(
- git_submodule **out,
- git_repository *repo,
- const char *url,
- const char *path,
- int use_gitlink)
-{
- int error = 0;
- git_config_backend *mods = NULL;
- git_submodule *sm = NULL;
- git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
- git_repository *subrepo = NULL;
-
- assert(repo && url && path);
-
- /* see if there is already an entry for this submodule */
-
- if (git_submodule_lookup(NULL, repo, path) < 0)
- giterr_clear();
- else {
- giterr_set(GITERR_SUBMODULE,
- "Attempt to add submodule '%s' that already exists", path);
- return GIT_EEXISTS;
- }
-
- /* validate and normalize path */
-
- if (git__prefixcmp(path, git_repository_workdir(repo)) == 0)
- path += strlen(git_repository_workdir(repo));
-
- if (git_path_root(path) >= 0) {
- giterr_set(GITERR_SUBMODULE, "Submodule path must be a relative path");
- error = -1;
- goto cleanup;
- }
-
- /* update .gitmodules */
-
- if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) {
- giterr_set(GITERR_SUBMODULE,
- "Adding submodules to a bare repository is not supported");
- return -1;
- }
-
- if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 ||
- (error = git_config_file_set_string(mods, name.ptr, path)) < 0)
- goto cleanup;
-
- if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
- (error = git_config_file_set_string(mods, name.ptr, url)) < 0)
- goto cleanup;
-
- git_buf_clear(&name);
-
- /* init submodule repository and add origin remote as needed */
-
- error = git_buf_joinpath(&name, git_repository_workdir(repo), path);
- if (error < 0)
- goto cleanup;
-
- /* if the repo does not already exist, then init a new repo and add it.
- * Otherwise, just add the existing repo.
- */
- if (!(git_path_exists(name.ptr) &&
- git_path_contains(&name, DOT_GIT))) {
-
- /* resolve the actual URL to use */
- if ((error = git_submodule_resolve_url(&real_url, repo, url)) < 0)
- goto cleanup;
-
- if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0)
- goto cleanup;
- }
-
- if ((error = git_submodule_lookup(&sm, repo, path)) < 0)
- goto cleanup;
-
- error = git_submodule_init(sm, false);
-
-cleanup:
- if (error && sm) {
- git_submodule_free(sm);
- sm = NULL;
- }
- if (out != NULL)
- *out = sm;
-
- git_config_file_free(mods);
- git_repository_free(subrepo);
- git_buf_free(&real_url);
- git_buf_free(&name);
-
- return error;
-}
-
-int git_submodule_repo_init(
- git_repository **out,
- const git_submodule *sm,
- int use_gitlink)
-{
- int error;
- git_repository *sub_repo = NULL;
- const char *configured_url;
- git_config *cfg = NULL;
- git_buf buf = GIT_BUF_INIT;
-
- assert(out && sm);
-
- /* get the configured remote url of the submodule */
- if ((error = git_buf_printf(&buf, "submodule.%s.url", sm->name)) < 0 ||
- (error = git_repository_config_snapshot(&cfg, sm->repo)) < 0 ||
- (error = git_config_get_string(&configured_url, cfg, buf.ptr)) < 0 ||
- (error = submodule_repo_init(&sub_repo, sm->repo, sm->path, configured_url, use_gitlink)) < 0)
- goto done;
-
- *out = sub_repo;
-
-done:
- git_config_free(cfg);
- git_buf_free(&buf);
- return error;
-}
-
-int git_submodule_add_finalize(git_submodule *sm)
-{
- int error;
- git_index *index;
-
- assert(sm);
-
- if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
- (error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0)
- return error;
-
- return git_submodule_add_to_index(sm, true);
-}
-
-int git_submodule_add_to_index(git_submodule *sm, int write_index)
-{
- int error;
- git_repository *sm_repo = NULL;
- git_index *index;
- git_buf path = GIT_BUF_INIT;
- git_commit *head;
- git_index_entry entry;
- struct stat st;
-
- assert(sm);
-
- /* force reload of wd OID by git_submodule_open */
- sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
-
- if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
- (error = git_buf_joinpath(
- &path, git_repository_workdir(sm->repo), sm->path)) < 0 ||
- (error = git_submodule_open(&sm_repo, sm)) < 0)
- goto cleanup;
-
- /* read stat information for submodule working directory */
- if (p_stat(path.ptr, &st) < 0) {
- giterr_set(GITERR_SUBMODULE,
- "Cannot add submodule without working directory");
- error = -1;
- goto cleanup;
- }
-
- memset(&entry, 0, sizeof(entry));
- entry.path = sm->path;
- git_index_entry__init_from_stat(
- &entry, &st, !(git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE));
-
- /* calling git_submodule_open will have set sm->wd_oid if possible */
- if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
- giterr_set(GITERR_SUBMODULE,
- "Cannot add submodule without HEAD to index");
- error = -1;
- goto cleanup;
- }
- git_oid_cpy(&entry.id, &sm->wd_oid);
-
- if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0)
- goto cleanup;
-
- entry.ctime.seconds = (int32_t)git_commit_time(head);
- entry.ctime.nanoseconds = 0;
- entry.mtime.seconds = (int32_t)git_commit_time(head);
- entry.mtime.nanoseconds = 0;
-
- git_commit_free(head);
-
- /* add it */
- error = git_index_add(index, &entry);
-
- /* write it, if requested */
- if (!error && write_index) {
- error = git_index_write(index);
-
- if (!error)
- git_oid_cpy(&sm->index_oid, &sm->wd_oid);
- }
-
-cleanup:
- git_repository_free(sm_repo);
- git_buf_free(&path);
- return error;
-}
-
-const char *git_submodule_update_to_str(git_submodule_update_t update)
-{
- int i;
- for (i = 0; i < (int)ARRAY_SIZE(_sm_update_map); ++i)
- if (_sm_update_map[i].map_value == (int)update)
- return _sm_update_map[i].str_match;
- return NULL;
-}
-
-git_repository *git_submodule_owner(git_submodule *submodule)
-{
- assert(submodule);
- return submodule->repo;
-}
-
-const char *git_submodule_name(git_submodule *submodule)
-{
- assert(submodule);
- return submodule->name;
-}
-
-const char *git_submodule_path(git_submodule *submodule)
-{
- assert(submodule);
- return submodule->path;
-}
-
-const char *git_submodule_url(git_submodule *submodule)
-{
- assert(submodule);
- return submodule->url;
-}
-
-int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url)
-{
- int error = 0;
- git_buf normalized = GIT_BUF_INIT;
-
- assert(out && repo && url);
-
- git_buf_sanitize(out);
-
- /* We do this in all platforms in case someone on Windows created the .gitmodules */
- if (strchr(url, '\\')) {
- if ((error = git_path_normalize_slashes(&normalized, url)) < 0)
- return error;
-
- url = normalized.ptr;
- }
-
-
- if (git_path_is_relative(url)) {
- if (!(error = get_url_base(out, repo)))
- error = git_path_apply_relative(out, url);
- } else if (strchr(url, ':') != NULL || url[0] == '/') {
- error = git_buf_sets(out, url);
- } else {
- giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL");
- error = -1;
- }
-
- git_buf_free(&normalized);
- return error;
-}
-
-static int write_var(git_repository *repo, const char *name, const char *var, const char *val)
-{
- git_buf key = GIT_BUF_INIT;
- git_config_backend *mods;
- int error;
-
- mods = open_gitmodules(repo, GITMODULES_CREATE);
- if (!mods)
- return -1;
-
- if ((error = git_buf_printf(&key, "submodule.%s.%s", name, var)) < 0)
- goto cleanup;
-
- if (val)
- error = git_config_file_set_string(mods, key.ptr, val);
- else
- error = git_config_file_delete(mods, key.ptr);
-
- git_buf_free(&key);
-
-cleanup:
- git_config_file_free(mods);
- return error;
-}
-
-static int write_mapped_var(git_repository *repo, const char *name, git_cvar_map *maps, size_t nmaps, const char *var, int ival)
-{
- git_cvar_t type;
- const char *val;
-
- if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) {
- giterr_set(GITERR_SUBMODULE, "invalid value for %s", var);
- return -1;
- }
-
- if (type == GIT_CVAR_TRUE)
- val = "true";
-
- return write_var(repo, name, var, val);
-}
-
-const char *git_submodule_branch(git_submodule *submodule)
-{
- assert(submodule);
- return submodule->branch;
-}
-
-int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch)
-{
-
- assert(repo && name);
-
- return write_var(repo, name, "branch", branch);
-}
-
-int git_submodule_set_url(git_repository *repo, const char *name, const char *url)
-{
- assert(repo && name && url);
-
- return write_var(repo, name, "url", url);
-}
-
-const git_oid *git_submodule_index_id(git_submodule *submodule)
-{
- assert(submodule);
-
- if (submodule->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID)
- return &submodule->index_oid;
- else
- return NULL;
-}
-
-const git_oid *git_submodule_head_id(git_submodule *submodule)
-{
- assert(submodule);
-
- if (submodule->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID)
- return &submodule->head_oid;
- else
- return NULL;
-}
-
-const git_oid *git_submodule_wd_id(git_submodule *submodule)
-{
- assert(submodule);
-
- /* load unless we think we have a valid oid */
- if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
- git_repository *subrepo;
-
- /* calling submodule open grabs the HEAD OID if possible */
- if (!git_submodule_open_bare(&subrepo, submodule))
- git_repository_free(subrepo);
- else
- giterr_clear();
- }
-
- if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)
- return &submodule->wd_oid;
- else
- return NULL;
-}
-
-git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
-{
- assert(submodule);
- return (submodule->ignore < GIT_SUBMODULE_IGNORE_NONE) ?
- GIT_SUBMODULE_IGNORE_NONE : submodule->ignore;
-}
-
-int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore)
-{
- assert(repo && name);
-
- return write_mapped_var(repo, name, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), "ignore", ignore);
-}
-
-git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule)
-{
- assert(submodule);
- return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ?
- GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update;
-}
-
-int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update)
-{
- assert(repo && name);
-
- return write_mapped_var(repo, name, _sm_update_map, ARRAY_SIZE(_sm_update_map), "update", update);
-}
-
-git_submodule_recurse_t git_submodule_fetch_recurse_submodules(
- git_submodule *submodule)
-{
- assert(submodule);
- return submodule->fetch_recurse;
-}
-
-int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse)
-{
- assert(repo && name);
-
- return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse);
-}
-
-static int submodule_repo_create(
- git_repository **out,
- git_repository *parent_repo,
- const char *path)
-{
- int error = 0;
- git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
- git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
- git_repository *subrepo = NULL;
-
- initopt.flags =
- GIT_REPOSITORY_INIT_MKPATH |
- GIT_REPOSITORY_INIT_NO_REINIT |
- GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
- GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
-
- /* Workdir: path to sub-repo working directory */
- error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
- if (error < 0)
- goto cleanup;
-
- initopt.workdir_path = workdir.ptr;
-
- /**
- * Repodir: path to the sub-repo. sub-repo goes in:
- * <repo-dir>/modules/<name>/ with a gitlink in the
- * sub-repo workdir directory to that repository.
- */
- error = git_buf_join3(
- &repodir, '/', git_repository_path(parent_repo), "modules", path);
- if (error < 0)
- goto cleanup;
-
- error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
-
-cleanup:
- git_buf_free(&workdir);
- git_buf_free(&repodir);
-
- *out = subrepo;
-
- return error;
-}
-
-/**
- * Callback to override sub-repository creation when
- * cloning a sub-repository.
- */
-static int git_submodule_update_repo_init_cb(
- git_repository **out,
- const char *path,
- int bare,
- void *payload)
-{
- git_submodule *sm;
-
- GIT_UNUSED(bare);
-
- sm = payload;
-
- return submodule_repo_create(out, sm->repo, path);
-}
-
-int git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_INIT);
- return 0;
-}
-
-int git_submodule_update(git_submodule *sm, int init, git_submodule_update_options *_update_options)
-{
- int error;
- unsigned int submodule_status;
- git_config *config = NULL;
- const char *submodule_url;
- git_repository *sub_repo = NULL;
- git_remote *remote = NULL;
- git_object *target_commit = NULL;
- git_buf buf = GIT_BUF_INIT;
- git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
- git_clone_options clone_options = GIT_CLONE_OPTIONS_INIT;
-
- assert(sm);
-
- if (_update_options)
- memcpy(&update_options, _update_options, sizeof(git_submodule_update_options));
-
- GITERR_CHECK_VERSION(&update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
-
- /* Copy over the remote callbacks */
- memcpy(&clone_options.fetch_opts, &update_options.fetch_opts, sizeof(git_fetch_options));
-
- /* Get the status of the submodule to determine if it is already initialized */
- if ((error = git_submodule_status(&submodule_status, sm->repo, sm->name, GIT_SUBMODULE_IGNORE_UNSPECIFIED)) < 0)
- goto done;
-
- /*
- * If submodule work dir is not already initialized, check to see
- * what we need to do (initialize, clone, return error...)
- */
- if (submodule_status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) {
- /*
- * Work dir is not initialized, check to see if the submodule
- * info has been copied into .git/config
- */
- if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
- (error = git_buf_printf(&buf, "submodule.%s.url", git_submodule_name(sm))) < 0)
- goto done;
-
- if ((error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0) {
- /*
- * If the error is not "not found" or if it is "not found" and we are not
- * initializing the submodule, then return error.
- */
- if (error != GIT_ENOTFOUND)
- goto done;
-
- if (!init) {
- giterr_set(GITERR_SUBMODULE, "Submodule is not initialized.");
- error = GIT_ERROR;
- goto done;
- }
-
- /* The submodule has not been initialized yet - initialize it now.*/
- if ((error = git_submodule_init(sm, 0)) < 0)
- goto done;
-
- git_config_free(config);
- config = NULL;
-
- if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
- (error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0)
- goto done;
- }
-
- /** submodule is initialized - now clone it **/
- /* override repo creation */
- clone_options.repository_cb = git_submodule_update_repo_init_cb;
- clone_options.repository_cb_payload = sm;
-
- /*
- * Do not perform checkout as part of clone, instead we
- * will checkout the specific commit manually.
- */
- clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
- update_options.checkout_opts.checkout_strategy = update_options.clone_checkout_strategy;
-
- if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
- (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 ||
- (error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0)
- goto done;
- } else {
- /**
- * Work dir is initialized - look up the commit in the parent repository's index,
- * update the workdir contents of the subrepository, and set the subrepository's
- * head to the new commit.
- */
- if ((error = git_submodule_open(&sub_repo, sm)) < 0)
- goto done;
-
- /* Look up the target commit in the submodule. */
- if ((error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0) {
- /* If it isn't found then fetch and try again. */
- if (error != GIT_ENOTFOUND || !update_options.allow_fetch ||
- (error = lookup_default_remote(&remote, sub_repo)) < 0 ||
- (error = git_remote_fetch(remote, NULL, &update_options.fetch_opts, NULL)) < 0 ||
- (error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0)
- goto done;
- }
-
- if ((error = git_checkout_tree(sub_repo, target_commit, &update_options.checkout_opts)) != 0 ||
- (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0)
- goto done;
-
- /* Invalidate the wd flags as the workdir has been updated. */
- sm->flags = sm->flags &
- ~(GIT_SUBMODULE_STATUS_IN_WD |
- GIT_SUBMODULE_STATUS__WD_OID_VALID |
- GIT_SUBMODULE_STATUS__WD_SCANNED);
- }
-
-done:
- git_buf_free(&buf);
- git_config_free(config);
- git_object_free(target_commit);
- git_remote_free(remote);
- git_repository_free(sub_repo);
-
- return error;
-}
-
-int git_submodule_init(git_submodule *sm, int overwrite)
-{
- int error;
- const char *val;
- git_buf key = GIT_BUF_INIT, effective_submodule_url = GIT_BUF_INIT;
- git_config *cfg = NULL;
-
- if (!sm->url) {
- giterr_set(GITERR_SUBMODULE,
- "No URL configured for submodule '%s'", sm->name);
- return -1;
- }
-
- if ((error = git_repository_config(&cfg, sm->repo)) < 0)
- return error;
-
- /* write "submodule.NAME.url" */
-
- if ((error = git_submodule_resolve_url(&effective_submodule_url, sm->repo, sm->url)) < 0 ||
- (error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 ||
- (error = git_config__update_entry(
- cfg, key.ptr, effective_submodule_url.ptr, overwrite != 0, false)) < 0)
- goto cleanup;
-
- /* write "submodule.NAME.update" if not default */
-
- val = (sm->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
- NULL : git_submodule_update_to_str(sm->update);
-
- if ((error = git_buf_printf(&key, "submodule.%s.update", sm->name)) < 0 ||
- (error = git_config__update_entry(
- cfg, key.ptr, val, overwrite != 0, false)) < 0)
- goto cleanup;
-
- /* success */
-
-cleanup:
- git_config_free(cfg);
- git_buf_free(&key);
- git_buf_free(&effective_submodule_url);
-
- return error;
-}
-
-int git_submodule_sync(git_submodule *sm)
-{
- int error = 0;
- git_config *cfg = NULL;
- git_buf key = GIT_BUF_INIT;
- git_repository *smrepo = NULL;
-
- if (!sm->url) {
- giterr_set(GITERR_SUBMODULE,
- "No URL configured for submodule '%s'", sm->name);
- return -1;
- }
-
- /* copy URL over to config only if it already exists */
-
- if (!(error = git_repository_config__weakptr(&cfg, sm->repo)) &&
- !(error = git_buf_printf(&key, "submodule.%s.url", sm->name)))
- error = git_config__update_entry(cfg, key.ptr, sm->url, true, true);
-
- /* if submodule exists in the working directory, update remote url */
-
- if (!error &&
- (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
- !(error = git_submodule_open(&smrepo, sm)))
- {
- git_buf remote_name = GIT_BUF_INIT;
-
- if ((error = git_repository_config__weakptr(&cfg, smrepo)) < 0)
- /* return error from reading submodule config */;
- else if ((error = lookup_head_remote_key(&remote_name, smrepo)) < 0) {
- giterr_clear();
- error = git_buf_sets(&key, "remote.origin.url");
- } else {
- error = git_buf_join3(
- &key, '.', "remote", remote_name.ptr, "url");
- git_buf_free(&remote_name);
- }
-
- if (!error)
- error = git_config__update_entry(cfg, key.ptr, sm->url, true, false);
-
- git_repository_free(smrepo);
- }
-
- git_buf_free(&key);
-
- return error;
-}
-
-static int git_submodule__open(
- git_repository **subrepo, git_submodule *sm, bool bare)
-{
- int error;
- git_buf path = GIT_BUF_INIT;
- unsigned int flags = GIT_REPOSITORY_OPEN_NO_SEARCH;
- const char *wd;
-
- assert(sm && subrepo);
-
- if (git_repository__ensure_not_bare(
- sm->repo, "open submodule repository") < 0)
- return GIT_EBAREREPO;
-
- wd = git_repository_workdir(sm->repo);
-
- if (git_buf_joinpath(&path, wd, sm->path) < 0 ||
- git_buf_joinpath(&path, path.ptr, DOT_GIT) < 0)
- return -1;
-
- sm->flags = sm->flags &
- ~(GIT_SUBMODULE_STATUS_IN_WD |
- GIT_SUBMODULE_STATUS__WD_OID_VALID |
- GIT_SUBMODULE_STATUS__WD_SCANNED);
-
- if (bare)
- flags |= GIT_REPOSITORY_OPEN_BARE;
-
- error = git_repository_open_ext(subrepo, path.ptr, flags, wd);
-
- /* if we opened the submodule successfully, grab HEAD OID, etc. */
- if (!error) {
- sm->flags |= GIT_SUBMODULE_STATUS_IN_WD |
- GIT_SUBMODULE_STATUS__WD_SCANNED;
-
- if (!git_reference_name_to_id(&sm->wd_oid, *subrepo, GIT_HEAD_FILE))
- sm->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
- else
- giterr_clear();
- } else if (git_path_exists(path.ptr)) {
- sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED |
- GIT_SUBMODULE_STATUS_IN_WD;
- } else {
- git_buf_rtruncate_at_char(&path, '/'); /* remove "/.git" */
-
- if (git_path_isdir(path.ptr))
- sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
- }
-
- git_buf_free(&path);
-
- return error;
-}
-
-int git_submodule_open_bare(git_repository **subrepo, git_submodule *sm)
-{
- return git_submodule__open(subrepo, sm, true);
-}
-
-int git_submodule_open(git_repository **subrepo, git_submodule *sm)
-{
- return git_submodule__open(subrepo, sm, false);
-}
-
-static void submodule_update_from_index_entry(
- git_submodule *sm, const git_index_entry *ie)
-{
- bool already_found = (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) != 0;
-
- if (!S_ISGITLINK(ie->mode)) {
- if (!already_found)
- sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
- } else {
- if (already_found)
- sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
- else
- git_oid_cpy(&sm->index_oid, &ie->id);
-
- sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX |
- GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
- }
-}
-
-static int submodule_update_index(git_submodule *sm)
-{
- git_index *index;
- const git_index_entry *ie;
-
- if (git_repository_index__weakptr(&index, sm->repo) < 0)
- return -1;
-
- sm->flags = sm->flags &
- ~(GIT_SUBMODULE_STATUS_IN_INDEX |
- GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
-
- if (!(ie = git_index_get_bypath(index, sm->path, 0)))
- return 0;
-
- submodule_update_from_index_entry(sm, ie);
-
- return 0;
-}
-
-static void submodule_update_from_head_data(
- git_submodule *sm, mode_t mode, const git_oid *id)
-{
- if (!S_ISGITLINK(mode))
- sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
- else {
- git_oid_cpy(&sm->head_oid, id);
-
- sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD |
- GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
- }
-}
-
-static int submodule_update_head(git_submodule *submodule)
-{
- git_tree *head = NULL;
- git_tree_entry *te = NULL;
-
- submodule->flags = submodule->flags &
- ~(GIT_SUBMODULE_STATUS_IN_HEAD |
- GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
-
- /* if we can't look up file in current head, then done */
- if (git_repository_head_tree(&head, submodule->repo) < 0 ||
- git_tree_entry_bypath(&te, head, submodule->path) < 0)
- giterr_clear();
- else
- submodule_update_from_head_data(submodule, te->attr, git_tree_entry_id(te));
-
- git_tree_entry_free(te);
- git_tree_free(head);
- return 0;
-}
-
-int git_submodule_reload(git_submodule *sm, int force)
-{
- int error = 0;
- git_config *mods;
-
- GIT_UNUSED(force);
-
- assert(sm);
-
- if (!git_repository_is_bare(sm->repo)) {
- /* refresh config data */
- mods = gitmodules_snapshot(sm->repo);
- if (mods != NULL) {
- error = submodule_read_config(sm, mods);
- git_config_free(mods);
-
- if (error < 0)
- return error;
- }
-
- /* refresh wd data */
- sm->flags &=
- ~(GIT_SUBMODULE_STATUS_IN_WD |
- GIT_SUBMODULE_STATUS__WD_OID_VALID |
- GIT_SUBMODULE_STATUS__WD_FLAGS);
-
- error = submodule_load_from_wd_lite(sm);
- }
-
- if (error == 0 && (error = submodule_update_index(sm)) == 0)
- error = submodule_update_head(sm);
-
- return error;
-}
-
-static void submodule_copy_oid_maybe(
- git_oid *tgt, const git_oid *src, bool valid)
-{
- if (tgt) {
- if (valid)
- memcpy(tgt, src, sizeof(*tgt));
- else
- memset(tgt, 0, sizeof(*tgt));
- }
-}
-
-int git_submodule__status(
- unsigned int *out_status,
- git_oid *out_head_id,
- git_oid *out_index_id,
- git_oid *out_wd_id,
- git_submodule *sm,
- git_submodule_ignore_t ign)
-{
- unsigned int status;
- git_repository *smrepo = NULL;
-
- if (ign == GIT_SUBMODULE_IGNORE_UNSPECIFIED)
- ign = sm->ignore;
-
- /* only return location info if ignore == all */
- if (ign == GIT_SUBMODULE_IGNORE_ALL) {
- *out_status = (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS);
- return 0;
- }
-
- /* refresh the index OID */
- if (submodule_update_index(sm) < 0)
- return -1;
-
- /* refresh the HEAD OID */
- if (submodule_update_head(sm) < 0)
- return -1;
-
- /* for ignore == dirty, don't scan the working directory */
- if (ign == GIT_SUBMODULE_IGNORE_DIRTY) {
- /* git_submodule_open_bare will load WD OID data */
- if (git_submodule_open_bare(&smrepo, sm) < 0)
- giterr_clear();
- else
- git_repository_free(smrepo);
- smrepo = NULL;
- } else if (git_submodule_open(&smrepo, sm) < 0) {
- giterr_clear();
- smrepo = NULL;
- }
-
- status = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(sm->flags);
-
- submodule_get_index_status(&status, sm);
- submodule_get_wd_status(&status, sm, smrepo, ign);
-
- git_repository_free(smrepo);
-
- *out_status = status;
-
- submodule_copy_oid_maybe(out_head_id, &sm->head_oid,
- (sm->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID) != 0);
- submodule_copy_oid_maybe(out_index_id, &sm->index_oid,
- (sm->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID) != 0);
- submodule_copy_oid_maybe(out_wd_id, &sm->wd_oid,
- (sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) != 0);
-
- return 0;
-}
-
-int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore)
-{
- git_submodule *sm;
- int error;
-
- assert(status && repo && name);
-
- if ((error = git_submodule_lookup(&sm, repo, name)) < 0)
- return error;
-
- error = git_submodule__status(status, NULL, NULL, NULL, sm, ignore);
- git_submodule_free(sm);
-
- return error;
-}
-
-int git_submodule_location(unsigned int *location, git_submodule *sm)
-{
- assert(location && sm);
-
- return git_submodule__status(
- location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
-}
-
-
-/*
- * INTERNAL FUNCTIONS
- */
-
-static int submodule_alloc(
- git_submodule **out, git_repository *repo, const char *name)
-{
- size_t namelen;
- git_submodule *sm;
-
- if (!name || !(namelen = strlen(name))) {
- giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
- return -1;
- }
-
- sm = git__calloc(1, sizeof(git_submodule));
- GITERR_CHECK_ALLOC(sm);
-
- sm->name = sm->path = git__strdup(name);
- if (!sm->name) {
- git__free(sm);
- return -1;
- }
-
- GIT_REFCOUNT_INC(sm);
- sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE;
- sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT;
- sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO;
- sm->repo = repo;
- sm->branch = NULL;
-
- *out = sm;
- return 0;
-}
-
-static void submodule_release(git_submodule *sm)
-{
- if (!sm)
- return;
-
- if (sm->repo) {
- sm->repo = NULL;
- }
-
- if (sm->path != sm->name)
- git__free(sm->path);
- git__free(sm->name);
- git__free(sm->url);
- git__free(sm->branch);
- git__memzero(sm, sizeof(*sm));
- git__free(sm);
-}
-
-void git_submodule_free(git_submodule *sm)
-{
- if (!sm)
- return;
- GIT_REFCOUNT_DEC(sm, submodule_release);
-}
-
-static int submodule_config_error(const char *property, const char *value)
-{
- giterr_set(GITERR_INVALID,
- "Invalid value for submodule '%s' property: '%s'", property, value);
- return -1;
-}
-
-int git_submodule_parse_ignore(git_submodule_ignore_t *out, const char *value)
-{
- int val;
-
- if (git_config_lookup_map_value(
- &val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0) {
- *out = GIT_SUBMODULE_IGNORE_NONE;
- return submodule_config_error("ignore", value);
- }
-
- *out = (git_submodule_ignore_t)val;
- return 0;
-}
-
-int git_submodule_parse_update(git_submodule_update_t *out, const char *value)
-{
- int val;
-
- if (git_config_lookup_map_value(
- &val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0) {
- *out = GIT_SUBMODULE_UPDATE_CHECKOUT;
- return submodule_config_error("update", value);
- }
-
- *out = (git_submodule_update_t)val;
- return 0;
-}
-
-int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value)
-{
- int val;
-
- if (git_config_lookup_map_value(
- &val, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), value) < 0) {
- *out = GIT_SUBMODULE_RECURSE_YES;
- return submodule_config_error("recurse", value);
- }
-
- *out = (git_submodule_recurse_t)val;
- return 0;
-}
-
-static int get_value(const char **out, git_config *cfg, git_buf *buf, const char *name, const char *field)
-{
- int error;
-
- git_buf_clear(buf);
-
- if ((error = git_buf_printf(buf, "submodule.%s.%s", name, field)) < 0 ||
- (error = git_config_get_string(out, cfg, buf->ptr)) < 0)
- return error;
-
- return error;
-}
-
-static int submodule_read_config(git_submodule *sm, git_config *cfg)
-{
- git_buf key = GIT_BUF_INIT;
- const char *value;
- int error, in_config = 0;
-
- /*
- * TODO: Look up path in index and if it is present but not a GITLINK
- * then this should be deleted (at least to match git's behavior)
- */
-
- if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) {
- in_config = 1;
- /*
- * TODO: if case insensitive filesystem, then the following strcmp
- * should be strcasecmp
- */
- if (strcmp(sm->name, value) != 0) {
- if (sm->path != sm->name)
- git__free(sm->path);
- sm->path = git__strdup(value);
- GITERR_CHECK_ALLOC(sm->path);
- }
- } else if (error != GIT_ENOTFOUND) {
- goto cleanup;
- }
-
- if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) {
- in_config = 1;
- sm->url = git__strdup(value);
- GITERR_CHECK_ALLOC(sm->url);
- } else if (error != GIT_ENOTFOUND) {
- goto cleanup;
- }
-
- if ((error = get_value(&value, cfg, &key, sm->name, "branch")) == 0) {
- in_config = 1;
- sm->branch = git__strdup(value);
- GITERR_CHECK_ALLOC(sm->branch);
- } else if (error != GIT_ENOTFOUND) {
- goto cleanup;
- }
-
- if ((error = get_value(&value, cfg, &key, sm->name, "update")) == 0) {
- in_config = 1;
- if ((error = git_submodule_parse_update(&sm->update, value)) < 0)
- goto cleanup;
- sm->update_default = sm->update;
- } else if (error != GIT_ENOTFOUND) {
- goto cleanup;
- }
-
- if ((error = get_value(&value, cfg, &key, sm->name, "fetchRecurseSubmodules")) == 0) {
- in_config = 1;
- if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0)
- goto cleanup;
- sm->fetch_recurse_default = sm->fetch_recurse;
- } else if (error != GIT_ENOTFOUND) {
- goto cleanup;
- }
-
- if ((error = get_value(&value, cfg, &key, sm->name, "ignore")) == 0) {
- in_config = 1;
- if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0)
- goto cleanup;
- sm->ignore_default = sm->ignore;
- } else if (error != GIT_ENOTFOUND) {
- goto cleanup;
- }
-
- if (in_config)
- sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
-
- error = 0;
-
-cleanup:
- git_buf_free(&key);
- return error;
-}
-
-static int submodule_load_each(const git_config_entry *entry, void *payload)
-{
- lfc_data *data = payload;
- const char *namestart, *property;
- git_strmap_iter pos;
- git_strmap *map = data->map;
- git_buf name = GIT_BUF_INIT;
- git_submodule *sm;
- int error;
-
- if (git__prefixcmp(entry->name, "submodule.") != 0)
- return 0;
-
- namestart = entry->name + strlen("submodule.");
- property = strrchr(namestart, '.');
-
- if (!property || (property == namestart))
- return 0;
-
- property++;
-
- if ((error = git_buf_set(&name, namestart, property - namestart -1)) < 0)
- return error;
-
- /*
- * Now that we have the submodule's name, we can use that to
- * figure out whether it's in the map. If it's not, we create
- * a new submodule, load the config and insert it. If it's
- * already inserted, we've already loaded it, so we skip.
- */
- pos = git_strmap_lookup_index(map, name.ptr);
- if (git_strmap_valid_index(map, pos)) {
- error = 0;
- goto done;
- }
-
- if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0)
- goto done;
-
- if ((error = submodule_read_config(sm, data->mods)) < 0) {
- git_submodule_free(sm);
- goto done;
- }
-
- git_strmap_insert(map, sm->name, sm, error);
- assert(error != 0);
- if (error < 0)
- goto done;
-
- error = 0;
-
-done:
- git_buf_free(&name);
- return error;
-}
-
-static int submodule_load_from_wd_lite(git_submodule *sm)
-{
- git_buf path = GIT_BUF_INIT;
-
- if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0)
- return -1;
-
- if (git_path_isdir(path.ptr))
- sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
-
- if (git_path_contains(&path, DOT_GIT))
- sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
-
- git_buf_free(&path);
- return 0;
-}
-
-/**
- * Returns a snapshot of $WORK_TREE/.gitmodules.
- *
- * We ignore any errors and just pretend the file isn't there.
- */
-static git_config *gitmodules_snapshot(git_repository *repo)
-{
- const char *workdir = git_repository_workdir(repo);
- git_config *mods = NULL, *snap = NULL;
- git_buf path = GIT_BUF_INIT;
-
- if (workdir != NULL) {
- if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
- return NULL;
-
- if (git_config_open_ondisk(&mods, path.ptr) < 0)
- mods = NULL;
- }
-
- git_buf_free(&path);
-
- if (mods) {
- git_config_snapshot(&snap, mods);
- git_config_free(mods);
- }
-
- return snap;
-}
-
-static git_config_backend *open_gitmodules(
- git_repository *repo,
- int okay_to_create)
-{
- const char *workdir = git_repository_workdir(repo);
- git_buf path = GIT_BUF_INIT;
- git_config_backend *mods = NULL;
-
- if (workdir != NULL) {
- if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
- return NULL;
-
- if (okay_to_create || git_path_isfile(path.ptr)) {
- /* git_config_file__ondisk should only fail if OOM */
- if (git_config_file__ondisk(&mods, path.ptr) < 0)
- mods = NULL;
- /* open should only fail here if the file is malformed */
- else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
- git_config_file_free(mods);
- mods = NULL;
- }
- }
- }
-
- git_buf_free(&path);
-
- return mods;
-}
-
-/* Lookup name of remote of the local tracking branch HEAD points to */
-static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo)
-{
- int error;
- git_reference *head = NULL;
- git_buf upstream_name = GIT_BUF_INIT;
-
- /* lookup and dereference HEAD */
- if ((error = git_repository_head(&head, repo)) < 0)
- return error;
-
- /**
- * If head does not refer to a branch, then return
- * GIT_ENOTFOUND to indicate that we could not find
- * a remote key for the local tracking branch HEAD points to.
- **/
- if (!git_reference_is_branch(head)) {
- giterr_set(GITERR_INVALID,
- "HEAD does not refer to a branch.");
- error = GIT_ENOTFOUND;
- goto done;
- }
-
- /* lookup remote tracking branch of HEAD */
- if ((error = git_branch_upstream_name(
- &upstream_name,
- repo,
- git_reference_name(head))) < 0)
- goto done;
-
- /* lookup remote of remote tracking branch */
- if ((error = git_branch_remote_name(remote_name, repo, upstream_name.ptr)) < 0)
- goto done;
-
-done:
- git_buf_free(&upstream_name);
- git_reference_free(head);
-
- return error;
-}
-
-/* Lookup the remote of the local tracking branch HEAD points to */
-static int lookup_head_remote(git_remote **remote, git_repository *repo)
-{
- int error;
- git_buf remote_name = GIT_BUF_INIT;
-
- /* lookup remote of remote tracking branch name */
- if (!(error = lookup_head_remote_key(&remote_name, repo)))
- error = git_remote_lookup(remote, repo, remote_name.ptr);
-
- git_buf_free(&remote_name);
-
- return error;
-}
-
-/* Lookup remote, either from HEAD or fall back on origin */
-static int lookup_default_remote(git_remote **remote, git_repository *repo)
-{
- int error = lookup_head_remote(remote, repo);
-
- /* if that failed, use 'origin' instead */
- if (error == GIT_ENOTFOUND)
- error = git_remote_lookup(remote, repo, "origin");
-
- if (error == GIT_ENOTFOUND)
- giterr_set(
- GITERR_SUBMODULE,
- "Cannot get default remote for submodule - no local tracking "
- "branch for HEAD and origin does not exist");
-
- return error;
-}
-
-static int get_url_base(git_buf *url, git_repository *repo)
-{
- int error;
- git_remote *remote = NULL;
-
- if (!(error = lookup_default_remote(&remote, repo))) {
- error = git_buf_sets(url, git_remote_url(remote));
- git_remote_free(remote);
- }
- else if (error == GIT_ENOTFOUND) {
- /* if repository does not have a default remote, use workdir instead */
- giterr_clear();
- error = git_buf_sets(url, git_repository_workdir(repo));
- }
-
- return error;
-}
-
-static void submodule_get_index_status(unsigned int *status, git_submodule *sm)
-{
- const git_oid *head_oid = git_submodule_head_id(sm);
- const git_oid *index_oid = git_submodule_index_id(sm);
-
- *status = *status & ~GIT_SUBMODULE_STATUS__INDEX_FLAGS;
-
- if (!head_oid) {
- if (index_oid)
- *status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
- }
- else if (!index_oid)
- *status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
- else if (!git_oid_equal(head_oid, index_oid))
- *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
-}
-
-
-static void submodule_get_wd_status(
- unsigned int *status,
- git_submodule *sm,
- git_repository *sm_repo,
- git_submodule_ignore_t ign)
-{
- const git_oid *index_oid = git_submodule_index_id(sm);
- const git_oid *wd_oid =
- (sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) ? &sm->wd_oid : NULL;
- git_tree *sm_head = NULL;
- git_index *index = NULL;
- git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
- git_diff *diff;
-
- *status = *status & ~GIT_SUBMODULE_STATUS__WD_FLAGS;
-
- if (!index_oid) {
- if (wd_oid)
- *status |= GIT_SUBMODULE_STATUS_WD_ADDED;
- }
- else if (!wd_oid) {
- if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 &&
- (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0)
- *status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED;
- else
- *status |= GIT_SUBMODULE_STATUS_WD_DELETED;
- }
- else if (!git_oid_equal(index_oid, wd_oid))
- *status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
-
- /* if we have no repo, then we're done */
- if (!sm_repo)
- return;
-
- /* the diffs below could be optimized with an early termination
- * option to the git_diff functions, but for now this is sufficient
- * (and certainly no worse that what core git does).
- */
-
- if (ign == GIT_SUBMODULE_IGNORE_NONE)
- opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
-
- (void)git_repository_index__weakptr(&index, sm_repo);
-
- /* if we don't have an unborn head, check diff with index */
- if (git_repository_head_tree(&sm_head, sm_repo) < 0)
- giterr_clear();
- else {
- /* perform head to index diff on submodule */
- if (git_diff_tree_to_index(&diff, sm_repo, sm_head, index, &opt) < 0)
- giterr_clear();
- else {
- if (git_diff_num_deltas(diff) > 0)
- *status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
- git_diff_free(diff);
- diff = NULL;
- }
-
- git_tree_free(sm_head);
- }
-
- /* perform index-to-workdir diff on submodule */
- if (git_diff_index_to_workdir(&diff, sm_repo, index, &opt) < 0)
- giterr_clear();
- else {
- size_t untracked =
- git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
-
- if (untracked > 0)
- *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
-
- if (git_diff_num_deltas(diff) != untracked)
- *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
-
- git_diff_free(diff);
- diff = NULL;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_submodule_h__
-#define INCLUDE_submodule_h__
-
-#include "git2/submodule.h"
-#include "git2/repository.h"
-#include "fileops.h"
-
-/* Notes:
- *
- * Submodule information can be in four places: the index, the config files
- * (both .git/config and .gitmodules), the HEAD tree, and the working
- * directory.
- *
- * In the index:
- * - submodule is found by path
- * - may be missing, present, or of the wrong type
- * - will have an oid if present
- *
- * In the HEAD tree:
- * - submodule is found by path
- * - may be missing, present, or of the wrong type
- * - will have an oid if present
- *
- * In the config files:
- * - submodule is found by submodule "name" which is usually the path
- * - may be missing or present
- * - will have a name, path, url, and other properties
- *
- * In the working directory:
- * - submodule is found by path
- * - may be missing, an empty directory, a checked out directory,
- * or of the wrong type
- * - if checked out, will have a HEAD oid
- * - if checked out, will have git history that can be used to compare oids
- * - if checked out, may have modified files and/or untracked files
- */
-
-/**
- * Description of submodule
- *
- * This record describes a submodule found in a repository. There should be
- * an entry for every submodule found in the HEAD and index, and for every
- * submodule described in .gitmodules. The fields are as follows:
- *
- * - `rc` tracks the refcount of how many hash table entries in the
- * git_submodule_cache there are for this submodule. It only comes into
- * play if the name and path of the submodule differ.
- *
- * - `name` is the name of the submodule from .gitmodules.
- * - `path` is the path to the submodule from the repo root. It is almost
- * always the same as `name`.
- * - `url` is the url for the submodule.
- * - `update` is a git_submodule_update_t value - see gitmodules(5) update.
- * - `update_default` is the update value from the config
- * - `ignore` is a git_submodule_ignore_t value - see gitmodules(5) ignore.
- * - `ignore_default` is the ignore value from the config
- * - `fetch_recurse` is a git_submodule_recurse_t value - see gitmodules(5)
- * fetchRecurseSubmodules.
- * - `fetch_recurse_default` is the recurse value from the config
- *
- * - `repo` is the parent repository that contains this submodule.
- * - `flags` after for internal use, tracking where this submodule has been
- * found (head, index, config, workdir) and known status info, etc.
- * - `head_oid` is the SHA1 for the submodule path in the repo HEAD.
- * - `index_oid` is the SHA1 for the submodule recorded in the index.
- * - `wd_oid` is the SHA1 for the HEAD of the checked out submodule.
- *
- * If the submodule has been added to .gitmodules but not yet git added,
- * then the `index_oid` will be zero but still marked valid. If the
- * submodule has been deleted, but the delete has not been committed yet,
- * then the `index_oid` will be set, but the `url` will be NULL.
- */
-struct git_submodule {
- git_refcount rc;
-
- /* information from config */
- char *name;
- char *path; /* important: may just point to "name" string */
- char *url;
- char *branch;
- git_submodule_update_t update;
- git_submodule_update_t update_default;
- git_submodule_ignore_t ignore;
- git_submodule_ignore_t ignore_default;
- git_submodule_recurse_t fetch_recurse;
- git_submodule_recurse_t fetch_recurse_default;
-
- /* internal information */
- git_repository *repo;
- uint32_t flags;
- git_oid head_oid;
- git_oid index_oid;
- git_oid wd_oid;
-};
-
-/* Force revalidation of submodule data cache (alloc as needed) */
-extern int git_submodule_cache_refresh(git_repository *repo);
-
-/* Release all submodules */
-extern void git_submodule_cache_free(git_repository *repo);
-
-/* Additional flags on top of public GIT_SUBMODULE_STATUS values */
-enum {
- GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20),
- GIT_SUBMODULE_STATUS__HEAD_OID_VALID = (1u << 21),
- GIT_SUBMODULE_STATUS__INDEX_OID_VALID = (1u << 22),
- GIT_SUBMODULE_STATUS__WD_OID_VALID = (1u << 23),
- GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE = (1u << 24),
- GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE = (1u << 25),
- GIT_SUBMODULE_STATUS__WD_NOT_SUBMODULE = (1u << 26),
- GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27),
-};
-
-#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
- ((S) & ~(0xFFFFFFFFu << 20))
-
-/* Internal lookup does not attempt to refresh cached data */
-extern int git_submodule__lookup(
- git_submodule **out, git_repository *repo, const char *path);
-
-/* Internal status fn returns status and optionally the various OIDs */
-extern int git_submodule__status(
- unsigned int *out_status,
- git_oid *out_head_id,
- git_oid *out_index_id,
- git_oid *out_wd_id,
- git_submodule *sm,
- git_submodule_ignore_t ign);
-
-/* Open submodule repository as bare repo for quick HEAD check, etc. */
-extern int git_submodule_open_bare(
- git_repository **repo,
- git_submodule *submodule);
-
-extern int git_submodule_parse_ignore(
- git_submodule_ignore_t *out, const char *value);
-extern int git_submodule_parse_update(
- git_submodule_update_t *out, const char *value);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "sysdir.h"
-#include "global.h"
-#include "buffer.h"
-#include "path.h"
-#include <ctype.h>
-#if GIT_WIN32
-#include "win32/findfile.h"
-#endif
-
-static int git_sysdir_guess_programdata_dirs(git_buf *out)
-{
-#ifdef GIT_WIN32
- return git_win32__find_programdata_dirs(out);
-#else
- git_buf_clear(out);
- return 0;
-#endif
-}
-
-static int git_sysdir_guess_system_dirs(git_buf *out)
-{
-#ifdef GIT_WIN32
- return git_win32__find_system_dirs(out, L"etc\\");
-#else
- return git_buf_sets(out, "/etc");
-#endif
-}
-
-static int git_sysdir_guess_global_dirs(git_buf *out)
-{
-#ifdef GIT_WIN32
- return git_win32__find_global_dirs(out);
-#else
- int error = git__getenv(out, "HOME");
-
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
-
- return error;
-#endif
-}
-
-static int git_sysdir_guess_xdg_dirs(git_buf *out)
-{
-#ifdef GIT_WIN32
- return git_win32__find_xdg_dirs(out);
-#else
- git_buf env = GIT_BUF_INIT;
- int error;
-
- if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0)
- error = git_buf_joinpath(out, env.ptr, "git");
-
- if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0)
- error = git_buf_joinpath(out, env.ptr, ".config/git");
-
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
-
- git_buf_free(&env);
- return error;
-#endif
-}
-
-static int git_sysdir_guess_template_dirs(git_buf *out)
-{
-#ifdef GIT_WIN32
- return git_win32__find_system_dirs(out, L"share\\git-core\\templates");
-#else
- return git_buf_sets(out, "/usr/share/git-core/templates");
-#endif
-}
-
-struct git_sysdir__dir {
- git_buf buf;
- int (*guess)(git_buf *out);
-};
-
-static struct git_sysdir__dir git_sysdir__dirs[] = {
- { GIT_BUF_INIT, git_sysdir_guess_system_dirs },
- { GIT_BUF_INIT, git_sysdir_guess_global_dirs },
- { GIT_BUF_INIT, git_sysdir_guess_xdg_dirs },
- { GIT_BUF_INIT, git_sysdir_guess_programdata_dirs },
- { GIT_BUF_INIT, git_sysdir_guess_template_dirs },
-};
-
-static void git_sysdir_global_shutdown(void)
-{
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(git_sysdir__dirs); ++i)
- git_buf_free(&git_sysdir__dirs[i].buf);
-}
-
-int git_sysdir_global_init(void)
-{
- size_t i;
- int error = 0;
-
- for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); i++)
- error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf);
-
- git__on_shutdown(git_sysdir_global_shutdown);
-
- return error;
-}
-
-static int git_sysdir_check_selector(git_sysdir_t which)
-{
- if (which < ARRAY_SIZE(git_sysdir__dirs))
- return 0;
-
- giterr_set(GITERR_INVALID, "config directory selector out of range");
- return -1;
-}
-
-
-int git_sysdir_get(const git_buf **out, git_sysdir_t which)
-{
- assert(out);
-
- *out = NULL;
-
- GITERR_CHECK_ERROR(git_sysdir_check_selector(which));
-
- *out = &git_sysdir__dirs[which].buf;
- return 0;
-}
-
-int git_sysdir_get_str(
- char *out,
- size_t outlen,
- git_sysdir_t which)
-{
- const git_buf *path = NULL;
-
- GITERR_CHECK_ERROR(git_sysdir_check_selector(which));
- GITERR_CHECK_ERROR(git_sysdir_get(&path, which));
-
- if (!out || path->size >= outlen) {
- giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path");
- return GIT_EBUFS;
- }
-
- git_buf_copy_cstr(out, outlen, path);
- return 0;
-}
-
-#define PATH_MAGIC "$PATH"
-
-int git_sysdir_set(git_sysdir_t which, const char *search_path)
-{
- const char *expand_path = NULL;
- git_buf merge = GIT_BUF_INIT;
-
- GITERR_CHECK_ERROR(git_sysdir_check_selector(which));
-
- if (search_path != NULL)
- expand_path = strstr(search_path, PATH_MAGIC);
-
- /* reset the default if this path has been cleared */
- if (!search_path)
- git_sysdir__dirs[which].guess(&git_sysdir__dirs[which].buf);
-
- /* if $PATH is not referenced, then just set the path */
- if (!expand_path) {
- if (search_path)
- git_buf_sets(&git_sysdir__dirs[which].buf, search_path);
-
- goto done;
- }
-
- /* otherwise set to join(before $PATH, old value, after $PATH) */
- if (expand_path > search_path)
- git_buf_set(&merge, search_path, expand_path - search_path);
-
- if (git_buf_len(&git_sysdir__dirs[which].buf))
- git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR,
- merge.ptr, git_sysdir__dirs[which].buf.ptr);
-
- expand_path += strlen(PATH_MAGIC);
- if (*expand_path)
- git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path);
-
- git_buf_swap(&git_sysdir__dirs[which].buf, &merge);
- git_buf_free(&merge);
-
-done:
- if (git_buf_oom(&git_sysdir__dirs[which].buf))
- return -1;
-
- return 0;
-}
-
-static int git_sysdir_find_in_dirlist(
- git_buf *path,
- const char *name,
- git_sysdir_t which,
- const char *label)
-{
- size_t len;
- const char *scan, *next = NULL;
- const git_buf *syspath;
-
- GITERR_CHECK_ERROR(git_sysdir_get(&syspath, which));
- if (!syspath || !git_buf_len(syspath))
- goto done;
-
- for (scan = git_buf_cstr(syspath); scan; scan = next) {
- /* find unescaped separator or end of string */
- for (next = scan; *next; ++next) {
- if (*next == GIT_PATH_LIST_SEPARATOR &&
- (next <= scan || next[-1] != '\\'))
- break;
- }
-
- len = (size_t)(next - scan);
- next = (*next ? next + 1 : NULL);
- if (!len)
- continue;
-
- GITERR_CHECK_ERROR(git_buf_set(path, scan, len));
- if (name)
- GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
-
- if (git_path_exists(path->ptr))
- return 0;
- }
-
-done:
- git_buf_free(path);
- giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name);
- return GIT_ENOTFOUND;
-}
-
-int git_sysdir_find_system_file(git_buf *path, const char *filename)
-{
- return git_sysdir_find_in_dirlist(
- path, filename, GIT_SYSDIR_SYSTEM, "system");
-}
-
-int git_sysdir_find_global_file(git_buf *path, const char *filename)
-{
- return git_sysdir_find_in_dirlist(
- path, filename, GIT_SYSDIR_GLOBAL, "global");
-}
-
-int git_sysdir_find_xdg_file(git_buf *path, const char *filename)
-{
- return git_sysdir_find_in_dirlist(
- path, filename, GIT_SYSDIR_XDG, "global/xdg");
-}
-
-int git_sysdir_find_programdata_file(git_buf *path, const char *filename)
-{
- return git_sysdir_find_in_dirlist(
- path, filename, GIT_SYSDIR_PROGRAMDATA, "ProgramData");
-}
-
-int git_sysdir_find_template_dir(git_buf *path)
-{
- return git_sysdir_find_in_dirlist(
- path, NULL, GIT_SYSDIR_TEMPLATE, "template");
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_sysdir_h__
-#define INCLUDE_sysdir_h__
-
-#include "common.h"
-#include "posix.h"
-#include "buffer.h"
-
-/**
- * Find a "global" file (i.e. one in a user's home directory).
- *
- * @param path buffer to write the full path into
- * @param filename name of file to find in the home directory
- * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
- */
-extern int git_sysdir_find_global_file(git_buf *path, const char *filename);
-
-/**
- * Find an "XDG" file (i.e. one in user's XDG config path).
- *
- * @param path buffer to write the full path into
- * @param filename name of file to find in the home directory
- * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
- */
-extern int git_sysdir_find_xdg_file(git_buf *path, const char *filename);
-
-/**
- * Find a "system" file (i.e. one shared for all users of the system).
- *
- * @param path buffer to write the full path into
- * @param filename name of file to find in the home directory
- * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
- */
-extern int git_sysdir_find_system_file(git_buf *path, const char *filename);
-
-/**
- * Find a "ProgramData" file (i.e. one in %PROGRAMDATA%)
- *
- * @param path buffer to write the full path into
- * @param filename name of file to find in the ProgramData directory
- * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
- */
-extern int git_sysdir_find_programdata_file(git_buf *path, const char *filename);
-
-/**
- * Find template directory.
- *
- * @param path buffer to write the full path into
- * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
- */
-extern int git_sysdir_find_template_dir(git_buf *path);
-
-typedef enum {
- GIT_SYSDIR_SYSTEM = 0,
- GIT_SYSDIR_GLOBAL = 1,
- GIT_SYSDIR_XDG = 2,
- GIT_SYSDIR_PROGRAMDATA = 3,
- GIT_SYSDIR_TEMPLATE = 4,
- GIT_SYSDIR__MAX = 5,
-} git_sysdir_t;
-
-/**
- * Configures global data for configuration file search paths.
- *
- * @return 0 on success, <0 on failure
- */
-extern int git_sysdir_global_init(void);
-
-/**
- * Get the search path for global/system/xdg files
- *
- * @param out pointer to git_buf containing search path
- * @param which which list of paths to return
- * @return 0 on success, <0 on failure
- */
-extern int git_sysdir_get(const git_buf **out, git_sysdir_t which);
-
-/**
- * Get search path into a preallocated buffer
- *
- * @param out String buffer to write into
- * @param outlen Size of string buffer
- * @param which Which search path to return
- * @return 0 on success, GIT_EBUFS if out is too small, <0 on other failure
- */
-
-extern int git_sysdir_get_str(char *out, size_t outlen, git_sysdir_t which);
-
-/**
- * Set search paths for global/system/xdg files
- *
- * The first occurrence of the magic string "$PATH" in the new value will
- * be replaced with the old value of the search path.
- *
- * @param which Which search path to modify
- * @param paths New search path (separated by GIT_PATH_LIST_SEPARATOR)
- * @return 0 on success, <0 on failure (allocation error)
- */
-extern int git_sysdir_set(git_sysdir_t which, const char *paths);
-
-#endif /* INCLUDE_sysdir_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "commit.h"
-#include "tag.h"
-#include "signature.h"
-#include "message.h"
-#include "git2/object.h"
-#include "git2/repository.h"
-#include "git2/signature.h"
-#include "git2/odb_backend.h"
-
-void git_tag__free(void *_tag)
-{
- git_tag *tag = _tag;
- git_signature_free(tag->tagger);
- git__free(tag->message);
- git__free(tag->tag_name);
- git__free(tag);
-}
-
-int git_tag_target(git_object **target, const git_tag *t)
-{
- assert(t);
- return git_object_lookup(target, t->object.repo, &t->target, t->type);
-}
-
-const git_oid *git_tag_target_id(const git_tag *t)
-{
- assert(t);
- return &t->target;
-}
-
-git_otype git_tag_target_type(const git_tag *t)
-{
- assert(t);
- return t->type;
-}
-
-const char *git_tag_name(const git_tag *t)
-{
- assert(t);
- return t->tag_name;
-}
-
-const git_signature *git_tag_tagger(const git_tag *t)
-{
- return t->tagger;
-}
-
-const char *git_tag_message(const git_tag *t)
-{
- assert(t);
- return t->message;
-}
-
-static int tag_error(const char *str)
-{
- giterr_set(GITERR_TAG, "Failed to parse tag. %s", str);
- return -1;
-}
-
-static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
-{
- static const char *tag_types[] = {
- NULL, "commit\n", "tree\n", "blob\n", "tag\n"
- };
-
- unsigned int i;
- size_t text_len, alloc_len;
- char *search;
-
- if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
- return tag_error("Object field invalid");
-
- if (buffer + 5 >= buffer_end)
- return tag_error("Object too short");
-
- if (memcmp(buffer, "type ", 5) != 0)
- return tag_error("Type field not found");
- buffer += 5;
-
- tag->type = GIT_OBJ_BAD;
-
- for (i = 1; i < ARRAY_SIZE(tag_types); ++i) {
- size_t type_length = strlen(tag_types[i]);
-
- if (buffer + type_length >= buffer_end)
- return tag_error("Object too short");
-
- if (memcmp(buffer, tag_types[i], type_length) == 0) {
- tag->type = i;
- buffer += type_length;
- break;
- }
- }
-
- if (tag->type == GIT_OBJ_BAD)
- return tag_error("Invalid object type");
-
- if (buffer + 4 >= buffer_end)
- return tag_error("Object too short");
-
- if (memcmp(buffer, "tag ", 4) != 0)
- return tag_error("Tag field not found");
-
- buffer += 4;
-
- search = memchr(buffer, '\n', buffer_end - buffer);
- if (search == NULL)
- return tag_error("Object too short");
-
- text_len = search - buffer;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
- tag->tag_name = git__malloc(alloc_len);
- GITERR_CHECK_ALLOC(tag->tag_name);
-
- memcpy(tag->tag_name, buffer, text_len);
- tag->tag_name[text_len] = '\0';
-
- buffer = search + 1;
-
- tag->tagger = NULL;
- if (buffer < buffer_end && *buffer != '\n') {
- tag->tagger = git__malloc(sizeof(git_signature));
- GITERR_CHECK_ALLOC(tag->tagger);
-
- if (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0)
- return -1;
- }
-
- tag->message = NULL;
- if (buffer < buffer_end) {
- /* If we're not at the end of the header, search for it */
- if( *buffer != '\n' ) {
- search = strstr(buffer, "\n\n");
- if (search)
- buffer = search + 1;
- else
- return tag_error("tag contains no message");
- }
-
- text_len = buffer_end - ++buffer;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
- tag->message = git__malloc(alloc_len);
- GITERR_CHECK_ALLOC(tag->message);
-
- memcpy(tag->message, buffer, text_len);
- tag->message[text_len] = '\0';
- }
-
- return 0;
-}
-
-int git_tag__parse(void *_tag, git_odb_object *odb_obj)
-{
- git_tag *tag = _tag;
- const char *buffer = git_odb_object_data(odb_obj);
- const char *buffer_end = buffer + git_odb_object_size(odb_obj);
-
- return tag_parse(tag, buffer, buffer_end);
-}
-
-static int retrieve_tag_reference(
- git_reference **tag_reference_out,
- git_buf *ref_name_out,
- git_repository *repo,
- const char *tag_name)
-{
- git_reference *tag_ref;
- int error;
-
- *tag_reference_out = NULL;
-
- if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
- return -1;
-
- error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
- if (error < 0)
- return error; /* Be it not foundo or corrupted */
-
- *tag_reference_out = tag_ref;
-
- return 0;
-}
-
-static int retrieve_tag_reference_oid(
- git_oid *oid,
- git_buf *ref_name_out,
- git_repository *repo,
- const char *tag_name)
-{
- if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
- return -1;
-
- return git_reference_name_to_id(oid, repo, ref_name_out->ptr);
-}
-
-static int write_tag_annotation(
- git_oid *oid,
- git_repository *repo,
- const char *tag_name,
- const git_object *target,
- const git_signature *tagger,
- const char *message)
-{
- git_buf tag = GIT_BUF_INIT;
- git_odb *odb;
-
- git_oid__writebuf(&tag, "object ", git_object_id(target));
- git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));
- git_buf_printf(&tag, "tag %s\n", tag_name);
- git_signature__writebuf(&tag, "tagger ", tagger);
- git_buf_putc(&tag, '\n');
-
- if (git_buf_puts(&tag, message) < 0)
- goto on_error;
-
- if (git_repository_odb__weakptr(&odb, repo) < 0)
- goto on_error;
-
- if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG) < 0)
- goto on_error;
-
- git_buf_free(&tag);
- return 0;
-
-on_error:
- git_buf_free(&tag);
- giterr_set(GITERR_OBJECT, "Failed to create tag annotation.");
- return -1;
-}
-
-static int git_tag_create__internal(
- git_oid *oid,
- git_repository *repo,
- const char *tag_name,
- const git_object *target,
- const git_signature *tagger,
- const char *message,
- int allow_ref_overwrite,
- int create_tag_annotation)
-{
- git_reference *new_ref = NULL;
- git_buf ref_name = GIT_BUF_INIT;
-
- int error;
-
- assert(repo && tag_name && target);
- assert(!create_tag_annotation || (tagger && message));
-
- if (git_object_owner(target) != repo) {
- giterr_set(GITERR_INVALID, "The given target does not belong to this repository");
- return -1;
- }
-
- error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- /** Ensure the tag name doesn't conflict with an already existing
- * reference unless overwriting has explicitly been requested **/
- if (error == 0 && !allow_ref_overwrite) {
- git_buf_free(&ref_name);
- giterr_set(GITERR_TAG, "Tag already exists");
- return GIT_EEXISTS;
- }
-
- if (create_tag_annotation) {
- if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
- return -1;
- } else
- git_oid_cpy(oid, git_object_id(target));
-
- error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
-
-cleanup:
- git_reference_free(new_ref);
- git_buf_free(&ref_name);
- return error;
-}
-
-int git_tag_create(
- git_oid *oid,
- git_repository *repo,
- const char *tag_name,
- const git_object *target,
- const git_signature *tagger,
- const char *message,
- int allow_ref_overwrite)
-{
- return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
-}
-
-int git_tag_annotation_create(
- git_oid *oid,
- git_repository *repo,
- const char *tag_name,
- const git_object *target,
- const git_signature *tagger,
- const char *message)
-{
- assert(oid && repo && tag_name && target && tagger && message);
-
- return write_tag_annotation(oid, repo, tag_name, target, tagger, message);
-}
-
-int git_tag_create_lightweight(
- git_oid *oid,
- git_repository *repo,
- const char *tag_name,
- const git_object *target,
- int allow_ref_overwrite)
-{
- return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
-}
-
-int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
-{
- git_tag tag;
- int error;
- git_odb *odb;
- git_odb_stream *stream;
- git_odb_object *target_obj;
-
- git_reference *new_ref = NULL;
- git_buf ref_name = GIT_BUF_INIT;
-
- assert(oid && buffer);
-
- memset(&tag, 0, sizeof(tag));
-
- if (git_repository_odb__weakptr(&odb, repo) < 0)
- return -1;
-
- /* validate the buffer */
- if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
- return -1;
-
- /* validate the target */
- if (git_odb_read(&target_obj, odb, &tag.target) < 0)
- goto on_error;
-
- if (tag.type != target_obj->cached.type) {
- giterr_set(GITERR_TAG, "The type for the given target is invalid");
- goto on_error;
- }
-
- error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
- if (error < 0 && error != GIT_ENOTFOUND)
- goto on_error;
-
- /* We don't need these objects after this */
- git_signature_free(tag.tagger);
- git__free(tag.tag_name);
- git__free(tag.message);
- git_odb_object_free(target_obj);
-
- /** Ensure the tag name doesn't conflict with an already existing
- * reference unless overwriting has explicitly been requested **/
- if (error == 0 && !allow_ref_overwrite) {
- giterr_set(GITERR_TAG, "Tag already exists");
- return GIT_EEXISTS;
- }
-
- /* write the buffer */
- if ((error = git_odb_open_wstream(
- &stream, odb, strlen(buffer), GIT_OBJ_TAG)) < 0)
- return error;
-
- if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
- error = git_odb_stream_finalize_write(oid, stream);
-
- git_odb_stream_free(stream);
-
- if (error < 0) {
- git_buf_free(&ref_name);
- return error;
- }
-
- error = git_reference_create(
- &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
-
- git_reference_free(new_ref);
- git_buf_free(&ref_name);
-
- return error;
-
-on_error:
- git_signature_free(tag.tagger);
- git__free(tag.tag_name);
- git__free(tag.message);
- git_odb_object_free(target_obj);
- return -1;
-}
-
-int git_tag_delete(git_repository *repo, const char *tag_name)
-{
- git_reference *tag_ref;
- git_buf ref_name = GIT_BUF_INIT;
- int error;
-
- error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
-
- git_buf_free(&ref_name);
-
- if (error < 0)
- return error;
-
- error = git_reference_delete(tag_ref);
-
- git_reference_free(tag_ref);
-
- return error;
-}
-
-typedef struct {
- git_repository *repo;
- git_tag_foreach_cb cb;
- void *cb_data;
-} tag_cb_data;
-
-static int tags_cb(const char *ref, void *data)
-{
- int error;
- git_oid oid;
- tag_cb_data *d = (tag_cb_data *)data;
-
- if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0)
- return 0; /* no tag */
-
- if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) {
- if ((error = d->cb(ref, &oid, d->cb_data)) != 0)
- giterr_set_after_callback_function(error, "git_tag_foreach");
- }
-
- return error;
-}
-
-int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
-{
- tag_cb_data data;
-
- assert(repo && cb);
-
- data.cb = cb;
- data.cb_data = cb_data;
- data.repo = repo;
-
- return git_reference_foreach_name(repo, &tags_cb, &data);
-}
-
-typedef struct {
- git_vector *taglist;
- const char *pattern;
-} tag_filter_data;
-
-#define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)
-
-static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
-{
- tag_filter_data *filter = (tag_filter_data *)data;
- GIT_UNUSED(oid);
-
- if (!*filter->pattern ||
- p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
- {
- char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN);
- GITERR_CHECK_ALLOC(matched);
-
- return git_vector_insert(filter->taglist, matched);
- }
-
- return 0;
-}
-
-int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
-{
- int error;
- tag_filter_data filter;
- git_vector taglist;
-
- assert(tag_names && repo && pattern);
-
- if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
- return error;
-
- filter.taglist = &taglist;
- filter.pattern = pattern;
-
- error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
-
- if (error < 0)
- git_vector_free(&taglist);
-
- tag_names->strings =
- (char **)git_vector_detach(&tag_names->count, NULL, &taglist);
-
- return 0;
-}
-
-int git_tag_list(git_strarray *tag_names, git_repository *repo)
-{
- return git_tag_list_match(tag_names, "", repo);
-}
-
-int git_tag_peel(git_object **tag_target, const git_tag *tag)
-{
- return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJ_ANY);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_tag_h__
-#define INCLUDE_tag_h__
-
-#include "git2/tag.h"
-#include "repository.h"
-#include "odb.h"
-
-struct git_tag {
- git_object object;
-
- git_oid target;
- git_otype type;
-
- char *tag_name;
- git_signature *tagger;
- char *message;
-};
-
-void git_tag__free(void *tag);
-int git_tag__parse(void *tag, git_odb_object *obj);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "thread-utils.h"
-
-#ifdef _WIN32
-#ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-#endif
-# include <windows.h>
-#elif defined(hpux) || defined(__hpux) || defined(_hpux)
-# include <sys/pstat.h>
-#endif
-
-/*
- * By doing this in two steps we can at least get
- * the function to be somewhat coherent, even
- * with this disgusting nest of #ifdefs.
- */
-#ifndef _SC_NPROCESSORS_ONLN
-# ifdef _SC_NPROC_ONLN
-# define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
-# elif defined _SC_CRAY_NCPU
-# define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
-# endif
-#endif
-
-int git_online_cpus(void)
-{
-#ifdef _SC_NPROCESSORS_ONLN
- long ncpus;
-#endif
-
-#ifdef _WIN32
- SYSTEM_INFO info;
- GetSystemInfo(&info);
-
- if ((int)info.dwNumberOfProcessors > 0)
- return (int)info.dwNumberOfProcessors;
-#elif defined(hpux) || defined(__hpux) || defined(_hpux)
- struct pst_dynamic psd;
-
- if (!pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0))
- return (int)psd.psd_proc_cnt;
-#endif
-
-#ifdef _SC_NPROCESSORS_ONLN
- if ((ncpus = (long)sysconf(_SC_NPROCESSORS_ONLN)) > 0)
- return (int)ncpus;
-#endif
-
- return 1;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_thread_utils_h__
-#define INCLUDE_thread_utils_h__
-
-/* Common operations even if threading has been disabled */
-typedef struct {
-#if defined(GIT_WIN32)
- volatile long val;
-#else
- volatile int val;
-#endif
-} git_atomic;
-
-#ifdef GIT_ARCH_64
-
-typedef struct {
-#if defined(GIT_WIN32)
- __int64 val;
-#else
- int64_t val;
-#endif
-} git_atomic64;
-
-typedef git_atomic64 git_atomic_ssize;
-
-#define git_atomic_ssize_add git_atomic64_add
-
-#else
-
-typedef git_atomic git_atomic_ssize;
-
-#define git_atomic_ssize_add git_atomic_add
-
-#endif
-
-#ifdef GIT_THREADS
-
-#ifdef GIT_WIN32
-# include "win32/thread.h"
-#else
-# include "unix/pthread.h"
-#endif
-
-GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
-{
-#if defined(GIT_WIN32)
- InterlockedExchange(&a->val, (LONG)val);
-#elif defined(__GNUC__)
- __sync_lock_test_and_set(&a->val, val);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(int) git_atomic_inc(git_atomic *a)
-{
-#if defined(GIT_WIN32)
- return InterlockedIncrement(&a->val);
-#elif defined(__GNUC__)
- return __sync_add_and_fetch(&a->val, 1);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend)
-{
-#if defined(GIT_WIN32)
- return InterlockedExchangeAdd(&a->val, addend);
-#elif defined(__GNUC__)
- return __sync_add_and_fetch(&a->val, addend);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(int) git_atomic_dec(git_atomic *a)
-{
-#if defined(GIT_WIN32)
- return InterlockedDecrement(&a->val);
-#elif defined(__GNUC__)
- return __sync_sub_and_fetch(&a->val, 1);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-GIT_INLINE(void *) git___compare_and_swap(
- void * volatile *ptr, void *oldval, void *newval)
-{
- volatile void *foundval;
-#if defined(GIT_WIN32)
- foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
-#elif defined(__GNUC__)
- foundval = __sync_val_compare_and_swap(ptr, oldval, newval);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
- return (foundval == oldval) ? oldval : newval;
-}
-
-GIT_INLINE(volatile void *) git___swap(
- void * volatile *ptr, void *newval)
-{
-#if defined(GIT_WIN32)
- return InterlockedExchangePointer(ptr, newval);
-#else
- return __sync_lock_test_and_set(ptr, newval);
-#endif
-}
-
-#ifdef GIT_ARCH_64
-
-GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
-{
-#if defined(GIT_WIN32)
- return InterlockedExchangeAdd64(&a->val, addend);
-#elif defined(__GNUC__)
- return __sync_add_and_fetch(&a->val, addend);
-#else
-# error "Unsupported architecture for atomic operations"
-#endif
-}
-
-#endif
-
-#else
-
-#define git_thread unsigned int
-#define git_thread_create(thread, start_routine, arg) 0
-#define git_thread_join(id, status) (void)0
-
-/* Pthreads Mutex */
-#define git_mutex unsigned int
-GIT_INLINE(int) git_mutex_init(git_mutex *mutex) \
- { GIT_UNUSED(mutex); return 0; }
-GIT_INLINE(int) git_mutex_lock(git_mutex *mutex) \
- { GIT_UNUSED(mutex); return 0; }
-#define git_mutex_unlock(a) (void)0
-#define git_mutex_free(a) (void)0
-
-/* Pthreads condition vars */
-#define git_cond unsigned int
-#define git_cond_init(c, a) (void)0
-#define git_cond_free(c) (void)0
-#define git_cond_wait(c, l) (void)0
-#define git_cond_signal(c) (void)0
-#define git_cond_broadcast(c) (void)0
-
-/* Pthreads rwlock */
-#define git_rwlock unsigned int
-#define git_rwlock_init(a) 0
-#define git_rwlock_rdlock(a) 0
-#define git_rwlock_rdunlock(a) (void)0
-#define git_rwlock_wrlock(a) 0
-#define git_rwlock_wrunlock(a) (void)0
-#define git_rwlock_free(a) (void)0
-#define GIT_RWLOCK_STATIC_INIT 0
-
-
-GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
-{
- a->val = val;
-}
-
-GIT_INLINE(int) git_atomic_inc(git_atomic *a)
-{
- return ++a->val;
-}
-
-GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend)
-{
- a->val += addend;
- return a->val;
-}
-
-GIT_INLINE(int) git_atomic_dec(git_atomic *a)
-{
- return --a->val;
-}
-
-GIT_INLINE(void *) git___compare_and_swap(
- void * volatile *ptr, void *oldval, void *newval)
-{
- if (*ptr == oldval)
- *ptr = newval;
- else
- oldval = newval;
- return oldval;
-}
-
-GIT_INLINE(volatile void *) git___swap(
- void * volatile *ptr, void *newval)
-{
- volatile void *old = *ptr;
- *ptr = newval;
- return old;
-}
-
-#ifdef GIT_ARCH_64
-
-GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
-{
- a->val += addend;
- return a->val;
-}
-
-#endif
-
-#endif
-
-GIT_INLINE(int) git_atomic_get(git_atomic *a)
-{
- return (int)a->val;
-}
-
-/* Atomically replace oldval with newval
- * @return oldval if it was replaced or newval if it was not
- */
-#define git__compare_and_swap(P,O,N) \
- git___compare_and_swap((void * volatile *)P, O, N)
-
-#define git__swap(ptr, val) (void *)git___swap((void * volatile *)&ptr, val)
-
-extern int git_online_cpus(void);
-
-#if defined(GIT_THREADS) && defined(_MSC_VER)
-# define GIT_MEMORY_BARRIER MemoryBarrier()
-#elif defined(GIT_THREADS)
-# define GIT_MEMORY_BARRIER __sync_synchronize()
-#else
-# define GIT_MEMORY_BARRIER /* noop */
-#endif
-
-#endif /* INCLUDE_thread_utils_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/errors.h"
-#include "common.h"
-
-#include "openssl_stream.h"
-#include "stransport_stream.h"
-
-static git_stream_cb tls_ctor;
-
-int git_stream_register_tls(git_stream_cb ctor)
-{
- tls_ctor = ctor;
-
- return 0;
-}
-
-int git_tls_stream_new(git_stream **out, const char *host, const char *port)
-{
-
- if (tls_ctor)
- return tls_ctor(out, host, port);
-
-#ifdef GIT_SECURE_TRANSPORT
- return git_stransport_stream_new(out, host, port);
-#elif defined(GIT_OPENSSL)
- return git_openssl_stream_new(out, host, port);
-#else
- GIT_UNUSED(out);
- GIT_UNUSED(host);
- GIT_UNUSED(port);
-
- giterr_set(GITERR_SSL, "there is no TLS stream available");
- return -1;
-#endif
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_tls_stream_h__
-#define INCLUDE_tls_stream_h__
-
-#include "git2/sys/stream.h"
-
-/**
- * Create a TLS stream with the most appropriate backend available for
- * the current platform.
- *
- * This allows us to ask for a SecureTransport or OpenSSL stream
- * according to being on general Unix vs OS X.
- */
-extern int git_tls_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "buffer.h"
-#include "common.h"
-#include "global.h"
-#include "trace.h"
-#include "git2/trace.h"
-
-#ifdef GIT_TRACE
-
-struct git_trace_data git_trace__data = {0};
-
-#endif
-
-int git_trace_set(git_trace_level_t level, git_trace_callback callback)
-{
-#ifdef GIT_TRACE
- assert(level == 0 || callback != NULL);
-
- git_trace__data.level = level;
- git_trace__data.callback = callback;
- GIT_MEMORY_BARRIER;
-
- return 0;
-#else
- GIT_UNUSED(level);
- GIT_UNUSED(callback);
-
- giterr_set(GITERR_INVALID,
- "This version of libgit2 was not built with tracing.");
- return -1;
-#endif
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_trace_h__
-#define INCLUDE_trace_h__
-
-#include <git2/trace.h>
-#include "buffer.h"
-
-#ifdef GIT_TRACE
-
-struct git_trace_data {
- git_trace_level_t level;
- git_trace_callback callback;
-};
-
-extern struct git_trace_data git_trace__data;
-
-GIT_INLINE(void) git_trace__write_fmt(
- git_trace_level_t level,
- const char *fmt, ...)
-{
- git_trace_callback callback = git_trace__data.callback;
- git_buf message = GIT_BUF_INIT;
- va_list ap;
-
- va_start(ap, fmt);
- git_buf_vprintf(&message, fmt, ap);
- va_end(ap);
-
- callback(level, git_buf_cstr(&message));
-
- git_buf_free(&message);
-}
-
-#define git_trace_level() (git_trace__data.level)
-#define git_trace(l, ...) { \
- if (git_trace__data.level >= l && \
- git_trace__data.callback != NULL) { \
- git_trace__write_fmt(l, __VA_ARGS__); \
- } \
- }
-
-#else
-
-GIT_INLINE(void) git_trace__null(
- git_trace_level_t level,
- const char *fmt, ...)
-{
- GIT_UNUSED(level);
- GIT_UNUSED(fmt);
-}
-
-#define git_trace_level() ((void)0)
-#define git_trace git_trace__null
-
-#endif
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "repository.h"
-#include "strmap.h"
-#include "refdb.h"
-#include "pool.h"
-#include "reflog.h"
-#include "signature.h"
-#include "config.h"
-
-#include "git2/transaction.h"
-#include "git2/signature.h"
-#include "git2/sys/refs.h"
-#include "git2/sys/refdb_backend.h"
-
-GIT__USE_STRMAP
-
-typedef enum {
- TRANSACTION_NONE,
- TRANSACTION_REFS,
- TRANSACTION_CONFIG,
-} transaction_t;
-
-typedef struct {
- const char *name;
- void *payload;
-
- git_ref_t ref_type;
- union {
- git_oid id;
- char *symbolic;
- } target;
- git_reflog *reflog;
-
- const char *message;
- git_signature *sig;
-
- unsigned int committed :1,
- remove :1;
-} transaction_node;
-
-struct git_transaction {
- transaction_t type;
- git_repository *repo;
- git_refdb *db;
- git_config *cfg;
-
- git_strmap *locks;
- git_pool pool;
-};
-
-int git_transaction_config_new(git_transaction **out, git_config *cfg)
-{
- git_transaction *tx;
- assert(out && cfg);
-
- tx = git__calloc(1, sizeof(git_transaction));
- GITERR_CHECK_ALLOC(tx);
-
- tx->type = TRANSACTION_CONFIG;
- tx->cfg = cfg;
- *out = tx;
- return 0;
-}
-
-int git_transaction_new(git_transaction **out, git_repository *repo)
-{
- int error;
- git_pool pool;
- git_transaction *tx = NULL;
-
- assert(out && repo);
-
- git_pool_init(&pool, 1);
-
- tx = git_pool_mallocz(&pool, sizeof(git_transaction));
- if (!tx) {
- error = -1;
- goto on_error;
- }
-
- if ((error = git_strmap_alloc(&tx->locks)) < 0) {
- error = -1;
- goto on_error;
- }
-
- if ((error = git_repository_refdb(&tx->db, repo)) < 0)
- goto on_error;
-
- tx->type = TRANSACTION_REFS;
- memcpy(&tx->pool, &pool, sizeof(git_pool));
- tx->repo = repo;
- *out = tx;
- return 0;
-
-on_error:
- git_pool_clear(&pool);
- return error;
-}
-
-int git_transaction_lock_ref(git_transaction *tx, const char *refname)
-{
- int error;
- transaction_node *node;
-
- assert(tx && refname);
-
- node = git_pool_mallocz(&tx->pool, sizeof(transaction_node));
- GITERR_CHECK_ALLOC(node);
-
- node->name = git_pool_strdup(&tx->pool, refname);
- GITERR_CHECK_ALLOC(node->name);
-
- if ((error = git_refdb_lock(&node->payload, tx->db, refname)) < 0)
- return error;
-
- git_strmap_insert(tx->locks, node->name, node, error);
- if (error < 0)
- goto cleanup;
-
- return 0;
-
-cleanup:
- git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL);
-
- return error;
-}
-
-static int find_locked(transaction_node **out, git_transaction *tx, const char *refname)
-{
- git_strmap_iter pos;
- transaction_node *node;
-
- pos = git_strmap_lookup_index(tx->locks, refname);
- if (!git_strmap_valid_index(tx->locks, pos)) {
- giterr_set(GITERR_REFERENCE, "the specified reference is not locked");
- return GIT_ENOTFOUND;
- }
-
- node = git_strmap_value_at(tx->locks, pos);
-
- *out = node;
- return 0;
-}
-
-static int copy_common(transaction_node *node, git_transaction *tx, const git_signature *sig, const char *msg)
-{
- if (sig && git_signature__pdup(&node->sig, sig, &tx->pool) < 0)
- return -1;
-
- if (!node->sig) {
- git_signature *tmp;
- int error;
-
- if (git_reference__log_signature(&tmp, tx->repo) < 0)
- return -1;
-
- /* make sure the sig we use is in our pool */
- error = git_signature__pdup(&node->sig, tmp, &tx->pool);
- git_signature_free(tmp);
- if (error < 0)
- return error;
- }
-
- if (msg) {
- node->message = git_pool_strdup(&tx->pool, msg);
- GITERR_CHECK_ALLOC(node->message);
- }
-
- return 0;
-}
-
-int git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg)
-{
- int error;
- transaction_node *node;
-
- assert(tx && refname && target);
-
- if ((error = find_locked(&node, tx, refname)) < 0)
- return error;
-
- if ((error = copy_common(node, tx, sig, msg)) < 0)
- return error;
-
- git_oid_cpy(&node->target.id, target);
- node->ref_type = GIT_REF_OID;
-
- return 0;
-}
-
-int git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg)
-{
- int error;
- transaction_node *node;
-
- assert(tx && refname && target);
-
- if ((error = find_locked(&node, tx, refname)) < 0)
- return error;
-
- if ((error = copy_common(node, tx, sig, msg)) < 0)
- return error;
-
- node->target.symbolic = git_pool_strdup(&tx->pool, target);
- GITERR_CHECK_ALLOC(node->target.symbolic);
- node->ref_type = GIT_REF_SYMBOLIC;
-
- return 0;
-}
-
-int git_transaction_remove(git_transaction *tx, const char *refname)
-{
- int error;
- transaction_node *node;
-
- if ((error = find_locked(&node, tx, refname)) < 0)
- return error;
-
- node->remove = true;
- node->ref_type = GIT_REF_OID; /* the id will be ignored */
-
- return 0;
-}
-
-static int dup_reflog(git_reflog **out, const git_reflog *in, git_pool *pool)
-{
- git_reflog *reflog;
- git_reflog_entry *entries;
- size_t len, i;
-
- reflog = git_pool_mallocz(pool, sizeof(git_reflog));
- GITERR_CHECK_ALLOC(reflog);
-
- reflog->ref_name = git_pool_strdup(pool, in->ref_name);
- GITERR_CHECK_ALLOC(reflog->ref_name);
-
- len = in->entries.length;
- reflog->entries.length = len;
- reflog->entries.contents = git_pool_mallocz(pool, len * sizeof(void *));
- GITERR_CHECK_ALLOC(reflog->entries.contents);
-
- entries = git_pool_mallocz(pool, len * sizeof(git_reflog_entry));
- GITERR_CHECK_ALLOC(entries);
-
- for (i = 0; i < len; i++) {
- const git_reflog_entry *src;
- git_reflog_entry *tgt;
-
- tgt = &entries[i];
- reflog->entries.contents[i] = tgt;
-
- src = git_vector_get(&in->entries, i);
- git_oid_cpy(&tgt->oid_old, &src->oid_old);
- git_oid_cpy(&tgt->oid_cur, &src->oid_cur);
-
- tgt->msg = git_pool_strdup(pool, src->msg);
- GITERR_CHECK_ALLOC(tgt->msg);
-
- if (git_signature__pdup(&tgt->committer, src->committer, pool) < 0)
- return -1;
- }
-
-
- *out = reflog;
- return 0;
-}
-
-int git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog)
-{
- int error;
- transaction_node *node;
-
- assert(tx && refname && reflog);
-
- if ((error = find_locked(&node, tx, refname)) < 0)
- return error;
-
- if ((error = dup_reflog(&node->reflog, reflog, &tx->pool)) < 0)
- return error;
-
- return 0;
-}
-
-static int update_target(git_refdb *db, transaction_node *node)
-{
- git_reference *ref;
- int error, update_reflog;
-
- if (node->ref_type == GIT_REF_OID) {
- ref = git_reference__alloc(node->name, &node->target.id, NULL);
- } else if (node->ref_type == GIT_REF_SYMBOLIC) {
- ref = git_reference__alloc_symbolic(node->name, node->target.symbolic);
- } else {
- abort();
- }
-
- GITERR_CHECK_ALLOC(ref);
- update_reflog = node->reflog == NULL;
-
- if (node->remove) {
- error = git_refdb_unlock(db, node->payload, 2, false, ref, NULL, NULL);
- } else if (node->ref_type == GIT_REF_OID) {
- error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message);
- } else if (node->ref_type == GIT_REF_SYMBOLIC) {
- error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message);
- } else {
- abort();
- }
-
- git_reference_free(ref);
- node->committed = true;
-
- return error;
-}
-
-int git_transaction_commit(git_transaction *tx)
-{
- transaction_node *node;
- git_strmap_iter pos;
- int error = 0;
-
- assert(tx);
-
- if (tx->type == TRANSACTION_CONFIG) {
- error = git_config_unlock(tx->cfg, true);
- tx->cfg = NULL;
-
- return error;
- }
-
- for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) {
- if (!git_strmap_has_data(tx->locks, pos))
- continue;
-
- node = git_strmap_value_at(tx->locks, pos);
- if (node->reflog) {
- if ((error = tx->db->backend->reflog_write(tx->db->backend, node->reflog)) < 0)
- return error;
- }
-
- if (node->ref_type != GIT_REF_INVALID) {
- if ((error = update_target(tx->db, node)) < 0)
- return error;
- }
- }
-
- return 0;
-}
-
-void git_transaction_free(git_transaction *tx)
-{
- transaction_node *node;
- git_pool pool;
- git_strmap_iter pos;
-
- assert(tx);
-
- if (tx->type == TRANSACTION_CONFIG) {
- if (tx->cfg) {
- git_config_unlock(tx->cfg, false);
- git_config_free(tx->cfg);
- }
-
- git__free(tx);
- return;
- }
-
- /* start by unlocking the ones we've left hanging, if any */
- for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) {
- if (!git_strmap_has_data(tx->locks, pos))
- continue;
-
- node = git_strmap_value_at(tx->locks, pos);
- if (node->committed)
- continue;
-
- git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL);
- }
-
- git_refdb_free(tx->db);
- git_strmap_free(tx->locks);
-
- /* tx is inside the pool, so we need to extract the data */
- memcpy(&pool, &tx->pool, sizeof(git_pool));
- git_pool_clear(&pool);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_transaction_h__
-#define INCLUDE_transaction_h__
-
-#include "common.h"
-
-int git_transaction_config_new(git_transaction **out, git_config *cfg);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "git2/types.h"
-#include "git2/remote.h"
-#include "git2/net.h"
-#include "git2/transport.h"
-#include "git2/sys/transport.h"
-#include "path.h"
-
-typedef struct transport_definition {
- char *prefix;
- git_transport_cb fn;
- void *param;
-} transport_definition;
-
-static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1, NULL };
-static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0, NULL };
-#ifdef GIT_SSH
-static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0, NULL };
-#endif
-
-static transport_definition local_transport_definition = { "file://", git_transport_local, NULL };
-
-static transport_definition transports[] = {
- { "git://", git_transport_smart, &git_subtransport_definition },
- { "http://", git_transport_smart, &http_subtransport_definition },
- { "https://", git_transport_smart, &http_subtransport_definition },
- { "file://", git_transport_local, NULL },
-#ifdef GIT_SSH
- { "ssh://", git_transport_smart, &ssh_subtransport_definition },
- { "ssh+git://", git_transport_smart, &ssh_subtransport_definition },
- { "git+ssh://", git_transport_smart, &ssh_subtransport_definition },
-#endif
- { NULL, 0, 0 }
-};
-
-static git_vector custom_transports = GIT_VECTOR_INIT;
-
-#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
-
-static transport_definition * transport_find_by_url(const char *url)
-{
- size_t i = 0;
- transport_definition *d;
-
- /* Find a user transport who wants to deal with this URI */
- git_vector_foreach(&custom_transports, i, d) {
- if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
- return d;
- }
- }
-
- /* Find a system transport for this URI */
- for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
- d = &transports[i];
-
- if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
- return d;
- }
- }
-
- return NULL;
-}
-
-static int transport_find_fn(
- git_transport_cb *out,
- const char *url,
- void **param)
-{
- transport_definition *definition = transport_find_by_url(url);
-
-#ifdef GIT_WIN32
- /* On Windows, it might not be possible to discern between absolute local
- * and ssh paths - first check if this is a valid local path that points
- * to a directory and if so assume local path, else assume SSH */
-
- /* Check to see if the path points to a file on the local file system */
- if (!definition && git_path_exists(url) && git_path_isdir(url))
- definition = &local_transport_definition;
-#endif
-
- /* For other systems, perform the SSH check first, to avoid going to the
- * filesystem if it is not necessary */
-
- /* It could be a SSH remote path. Check to see if there's a : */
- if (!definition && strrchr(url, ':')) {
- /* re-search transports again with ssh:// as url
- * so that we can find a third party ssh transport */
- definition = transport_find_by_url("ssh://");
- }
-
-#ifndef GIT_WIN32
- /* Check to see if the path points to a file on the local file system */
- if (!definition && git_path_exists(url) && git_path_isdir(url))
- definition = &local_transport_definition;
-#endif
-
- if (!definition)
- return GIT_ENOTFOUND;
-
- *out = definition->fn;
- *param = definition->param;
-
- return 0;
-}
-
-/**************
- * Public API *
- **************/
-
-int git_transport_new(git_transport **out, git_remote *owner, const char *url)
-{
- git_transport_cb fn;
- git_transport *transport;
- void *param;
- int error;
-
- if ((error = transport_find_fn(&fn, url, ¶m)) == GIT_ENOTFOUND) {
- giterr_set(GITERR_NET, "Unsupported URL protocol");
- return -1;
- } else if (error < 0)
- return error;
-
- if ((error = fn(&transport, owner, param)) < 0)
- return error;
-
- GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
-
- *out = transport;
-
- return 0;
-}
-
-int git_transport_register(
- const char *scheme,
- git_transport_cb cb,
- void *param)
-{
- git_buf prefix = GIT_BUF_INIT;
- transport_definition *d, *definition = NULL;
- size_t i;
- int error = 0;
-
- assert(scheme);
- assert(cb);
-
- if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0)
- goto on_error;
-
- git_vector_foreach(&custom_transports, i, d) {
- if (strcasecmp(d->prefix, prefix.ptr) == 0) {
- error = GIT_EEXISTS;
- goto on_error;
- }
- }
-
- definition = git__calloc(1, sizeof(transport_definition));
- GITERR_CHECK_ALLOC(definition);
-
- definition->prefix = git_buf_detach(&prefix);
- definition->fn = cb;
- definition->param = param;
-
- if (git_vector_insert(&custom_transports, definition) < 0)
- goto on_error;
-
- return 0;
-
-on_error:
- git_buf_free(&prefix);
- git__free(definition);
- return error;
-}
-
-int git_transport_unregister(const char *scheme)
-{
- git_buf prefix = GIT_BUF_INIT;
- transport_definition *d;
- size_t i;
- int error = 0;
-
- assert(scheme);
-
- if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0)
- goto done;
-
- git_vector_foreach(&custom_transports, i, d) {
- if (strcasecmp(d->prefix, prefix.ptr) == 0) {
- if ((error = git_vector_remove(&custom_transports, i)) < 0)
- goto done;
-
- git__free(d->prefix);
- git__free(d);
-
- if (!custom_transports.length)
- git_vector_free(&custom_transports);
-
- error = 0;
- goto done;
- }
- }
-
- error = GIT_ENOTFOUND;
-
-done:
- git_buf_free(&prefix);
- return error;
-}
-
-int git_transport_init(git_transport *opts, unsigned int version)
-{
- GIT_INIT_STRUCTURE_FROM_TEMPLATE(
- opts, version, git_transport, GIT_TRANSPORT_INIT);
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2.h"
-#include "buffer.h"
-#include "auth.h"
-
-static int basic_next_token(
- git_buf *out, git_http_auth_context *ctx, git_cred *c)
-{
- git_cred_userpass_plaintext *cred;
- git_buf raw = GIT_BUF_INIT;
- int error = -1;
-
- GIT_UNUSED(ctx);
-
- if (c->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
- giterr_set(GITERR_INVALID, "invalid credential type for basic auth");
- goto on_error;
- }
-
- cred = (git_cred_userpass_plaintext *)c;
-
- git_buf_printf(&raw, "%s:%s", cred->username, cred->password);
-
- if (git_buf_oom(&raw) ||
- git_buf_puts(out, "Authorization: Basic ") < 0 ||
- git_buf_encode_base64(out, git_buf_cstr(&raw), raw.size) < 0 ||
- git_buf_puts(out, "\r\n") < 0)
- goto on_error;
-
- error = 0;
-
-on_error:
- if (raw.size)
- git__memzero(raw.ptr, raw.size);
-
- git_buf_free(&raw);
- return error;
-}
-
-static git_http_auth_context basic_context = {
- GIT_AUTHTYPE_BASIC,
- GIT_CREDTYPE_USERPASS_PLAINTEXT,
- NULL,
- basic_next_token,
- NULL
-};
-
-int git_http_auth_basic(
- git_http_auth_context **out, const gitno_connection_data *connection_data)
-{
- GIT_UNUSED(connection_data);
-
- *out = &basic_context;
- return 0;
-}
-
-int git_http_auth_dummy(
- git_http_auth_context **out, const gitno_connection_data *connection_data)
-{
- GIT_UNUSED(connection_data);
-
- *out = NULL;
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_http_auth_h__
-#define INCLUDE_http_auth_h__
-
-#include "git2.h"
-#include "netops.h"
-
-typedef enum {
- GIT_AUTHTYPE_BASIC = 1,
- GIT_AUTHTYPE_NEGOTIATE = 2,
-} git_http_authtype_t;
-
-typedef struct git_http_auth_context git_http_auth_context;
-
-struct git_http_auth_context {
- /** Type of scheme */
- git_http_authtype_t type;
-
- /** Supported credentials */
- git_credtype_t credtypes;
-
- /** Sets the challenge on the authentication context */
- int (*set_challenge)(git_http_auth_context *ctx, const char *challenge);
-
- /** Gets the next authentication token from the context */
- int (*next_token)(git_buf *out, git_http_auth_context *ctx, git_cred *cred);
-
- /** Frees the authentication context */
- void (*free)(git_http_auth_context *ctx);
-};
-
-typedef struct {
- /** Type of scheme */
- git_http_authtype_t type;
-
- /** Name of the scheme (as used in the Authorization header) */
- const char *name;
-
- /** Credential types this scheme supports */
- git_credtype_t credtypes;
-
- /** Function to initialize an authentication context */
- int (*init_context)(
- git_http_auth_context **out,
- const gitno_connection_data *connection_data);
-} git_http_auth_scheme;
-
-int git_http_auth_dummy(
- git_http_auth_context **out,
- const gitno_connection_data *connection_data);
-
-int git_http_auth_basic(
- git_http_auth_context **out,
- const gitno_connection_data *connection_data);
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifdef GIT_GSSAPI
-
-#include "git2.h"
-#include "common.h"
-#include "buffer.h"
-#include "auth.h"
-
-#include <gssapi.h>
-#include <krb5.h>
-
-static gss_OID_desc negotiate_oid_spnego =
- { 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
-static gss_OID_desc negotiate_oid_krb5 =
- { 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
-
-static gss_OID negotiate_oids[] =
- { &negotiate_oid_spnego, &negotiate_oid_krb5, NULL };
-
-typedef struct {
- git_http_auth_context parent;
- unsigned configured : 1,
- complete : 1;
- git_buf target;
- char *challenge;
- gss_ctx_id_t gss_context;
- gss_OID oid;
-} http_auth_negotiate_context;
-
-static void negotiate_err_set(
- OM_uint32 status_major,
- OM_uint32 status_minor,
- const char *message)
-{
- gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
- OM_uint32 status_display, context = 0;
-
- if (gss_display_status(&status_display, status_major, GSS_C_GSS_CODE,
- GSS_C_NO_OID, &context, &buffer) == GSS_S_COMPLETE) {
- giterr_set(GITERR_NET, "%s: %.*s (%d.%d)",
- message, (int)buffer.length, (const char *)buffer.value,
- status_major, status_minor);
- gss_release_buffer(&status_minor, &buffer);
- } else {
- giterr_set(GITERR_NET, "%s: unknown negotiate error (%d.%d)",
- message, status_major, status_minor);
- }
-}
-
-static int negotiate_set_challenge(
- git_http_auth_context *c,
- const char *challenge)
-{
- http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
-
- assert(ctx && ctx->configured && challenge);
-
- git__free(ctx->challenge);
-
- ctx->challenge = git__strdup(challenge);
- GITERR_CHECK_ALLOC(ctx->challenge);
-
- return 0;
-}
-
-static int negotiate_next_token(
- git_buf *buf,
- git_http_auth_context *c,
- git_cred *cred)
-{
- http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
- OM_uint32 status_major, status_minor;
- gss_buffer_desc target_buffer = GSS_C_EMPTY_BUFFER,
- input_token = GSS_C_EMPTY_BUFFER,
- output_token = GSS_C_EMPTY_BUFFER;
- gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
- git_buf input_buf = GIT_BUF_INIT;
- gss_name_t server = NULL;
- gss_OID mech;
- size_t challenge_len;
- int error = 0;
-
- assert(buf && ctx && ctx->configured && cred && cred->credtype == GIT_CREDTYPE_DEFAULT);
-
- if (ctx->complete)
- return 0;
-
- target_buffer.value = (void *)ctx->target.ptr;
- target_buffer.length = ctx->target.size;
-
- status_major = gss_import_name(&status_minor, &target_buffer,
- GSS_C_NT_HOSTBASED_SERVICE, &server);
-
- if (GSS_ERROR(status_major)) {
- negotiate_err_set(status_major, status_minor,
- "Could not parse principal");
- error = -1;
- goto done;
- }
-
- challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
-
- if (challenge_len < 9) {
- giterr_set(GITERR_NET, "No negotiate challenge sent from server");
- error = -1;
- goto done;
- } else if (challenge_len > 9) {
- if (git_buf_decode_base64(&input_buf,
- ctx->challenge + 10, challenge_len - 10) < 0) {
- giterr_set(GITERR_NET, "Invalid negotiate challenge from server");
- error = -1;
- goto done;
- }
-
- input_token.value = input_buf.ptr;
- input_token.length = input_buf.size;
- input_token_ptr = &input_token;
- } else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
- giterr_set(GITERR_NET, "Could not restart authentication");
- error = -1;
- goto done;
- }
-
- mech = &negotiate_oid_spnego;
-
- if (GSS_ERROR(status_major = gss_init_sec_context(
- &status_minor,
- GSS_C_NO_CREDENTIAL,
- &ctx->gss_context,
- server,
- mech,
- GSS_C_DELEG_FLAG | GSS_C_MUTUAL_FLAG,
- GSS_C_INDEFINITE,
- GSS_C_NO_CHANNEL_BINDINGS,
- input_token_ptr,
- NULL,
- &output_token,
- NULL,
- NULL))) {
- negotiate_err_set(status_major, status_minor, "Negotiate failure");
- error = -1;
- goto done;
- }
-
- /* This message merely told us auth was complete; we do not respond. */
- if (status_major == GSS_S_COMPLETE) {
- ctx->complete = 1;
- goto done;
- }
-
- git_buf_puts(buf, "Authorization: Negotiate ");
- git_buf_encode_base64(buf, output_token.value, output_token.length);
- git_buf_puts(buf, "\r\n");
-
- if (git_buf_oom(buf))
- error = -1;
-
-done:
- gss_release_name(&status_minor, &server);
- gss_release_buffer(&status_minor, (gss_buffer_t) &output_token);
- git_buf_free(&input_buf);
- return error;
-}
-
-static void negotiate_context_free(git_http_auth_context *c)
-{
- http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
- OM_uint32 status_minor;
-
- if (ctx->gss_context != GSS_C_NO_CONTEXT) {
- gss_delete_sec_context(
- &status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
- ctx->gss_context = GSS_C_NO_CONTEXT;
- }
-
- git_buf_free(&ctx->target);
-
- git__free(ctx->challenge);
-
- ctx->configured = 0;
- ctx->complete = 0;
- ctx->oid = NULL;
-
- git__free(ctx);
-}
-
-static int negotiate_init_context(
- http_auth_negotiate_context *ctx,
- const gitno_connection_data *connection_data)
-{
- OM_uint32 status_major, status_minor;
- gss_OID item, *oid;
- gss_OID_set mechanism_list;
- size_t i;
-
- /* Query supported mechanisms looking for SPNEGO) */
- if (GSS_ERROR(status_major =
- gss_indicate_mechs(&status_minor, &mechanism_list))) {
- negotiate_err_set(status_major, status_minor,
- "could not query mechanisms");
- return -1;
- }
-
- if (mechanism_list) {
- for (oid = negotiate_oids; *oid; oid++) {
- for (i = 0; i < mechanism_list->count; i++) {
- item = &mechanism_list->elements[i];
-
- if (item->length == (*oid)->length &&
- memcmp(item->elements, (*oid)->elements, item->length) == 0) {
- ctx->oid = *oid;
- break;
- }
-
- }
-
- if (ctx->oid)
- break;
- }
- }
-
- gss_release_oid_set(&status_minor, &mechanism_list);
-
- if (!ctx->oid) {
- giterr_set(GITERR_NET, "Negotiate authentication is not supported");
- return -1;
- }
-
- git_buf_puts(&ctx->target, "HTTP@");
- git_buf_puts(&ctx->target, connection_data->host);
-
- if (git_buf_oom(&ctx->target))
- return -1;
-
- ctx->gss_context = GSS_C_NO_CONTEXT;
- ctx->configured = 1;
-
- return 0;
-}
-
-int git_http_auth_negotiate(
- git_http_auth_context **out,
- const gitno_connection_data *connection_data)
-{
- http_auth_negotiate_context *ctx;
-
- *out = NULL;
-
- ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
- GITERR_CHECK_ALLOC(ctx);
-
- if (negotiate_init_context(ctx, connection_data) < 0) {
- git__free(ctx);
- return -1;
- }
-
- ctx->parent.type = GIT_AUTHTYPE_NEGOTIATE;
- ctx->parent.credtypes = GIT_CREDTYPE_DEFAULT;
- ctx->parent.set_challenge = negotiate_set_challenge;
- ctx->parent.next_token = negotiate_next_token;
- ctx->parent.free = negotiate_context_free;
-
- *out = (git_http_auth_context *)ctx;
-
- return 0;
-}
-
-#endif /* GIT_GSSAPI */
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_auth_negotiate_h__
-#define INCLUDE_auth_negotiate_h__
-
-#include "git2.h"
-#include "auth.h"
-
-#ifdef GIT_GSSAPI
-
-extern int git_http_auth_negotiate(
- git_http_auth_context **out,
- const gitno_connection_data *connection_data);
-
-#else
-
-#define git_http_auth_negotiate git_http_auth_dummy
-
-#endif /* GIT_GSSAPI */
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2.h"
-#include "smart.h"
-#include "git2/cred_helpers.h"
-
-static int git_cred_ssh_key_type_new(
- git_cred **cred,
- const char *username,
- const char *publickey,
- const char *privatekey,
- const char *passphrase,
- git_credtype_t credtype);
-
-int git_cred_has_username(git_cred *cred)
-{
- if (cred->credtype == GIT_CREDTYPE_DEFAULT)
- return 0;
-
- return 1;
-}
-
-const char *git_cred__username(git_cred *cred)
-{
- switch (cred->credtype) {
- case GIT_CREDTYPE_USERNAME:
- {
- git_cred_username *c = (git_cred_username *) cred;
- return c->username;
- }
- case GIT_CREDTYPE_USERPASS_PLAINTEXT:
- {
- git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *) cred;
- return c->username;
- }
- case GIT_CREDTYPE_SSH_KEY:
- case GIT_CREDTYPE_SSH_MEMORY:
- {
- git_cred_ssh_key *c = (git_cred_ssh_key *) cred;
- return c->username;
- }
- case GIT_CREDTYPE_SSH_CUSTOM:
- {
- git_cred_ssh_custom *c = (git_cred_ssh_custom *) cred;
- return c->username;
- }
- case GIT_CREDTYPE_SSH_INTERACTIVE:
- {
- git_cred_ssh_interactive *c = (git_cred_ssh_interactive *) cred;
- return c->username;
- }
-
- default:
- return NULL;
- }
-}
-
-static void plaintext_free(struct git_cred *cred)
-{
- git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
-
- git__free(c->username);
-
- /* Zero the memory which previously held the password */
- if (c->password) {
- size_t pass_len = strlen(c->password);
- git__memzero(c->password, pass_len);
- git__free(c->password);
- }
-
- git__free(c);
-}
-
-int git_cred_userpass_plaintext_new(
- git_cred **cred,
- const char *username,
- const char *password)
-{
- git_cred_userpass_plaintext *c;
-
- assert(cred && username && password);
-
- c = git__malloc(sizeof(git_cred_userpass_plaintext));
- GITERR_CHECK_ALLOC(c);
-
- c->parent.credtype = GIT_CREDTYPE_USERPASS_PLAINTEXT;
- c->parent.free = plaintext_free;
- c->username = git__strdup(username);
-
- if (!c->username) {
- git__free(c);
- return -1;
- }
-
- c->password = git__strdup(password);
-
- if (!c->password) {
- git__free(c->username);
- git__free(c);
- return -1;
- }
-
- *cred = &c->parent;
- return 0;
-}
-
-static void ssh_key_free(struct git_cred *cred)
-{
- git_cred_ssh_key *c =
- (git_cred_ssh_key *)cred;
-
- git__free(c->username);
-
- if (c->privatekey) {
- /* Zero the memory which previously held the private key */
- size_t key_len = strlen(c->privatekey);
- git__memzero(c->privatekey, key_len);
- git__free(c->privatekey);
- }
-
- if (c->passphrase) {
- /* Zero the memory which previously held the passphrase */
- size_t pass_len = strlen(c->passphrase);
- git__memzero(c->passphrase, pass_len);
- git__free(c->passphrase);
- }
-
- if (c->publickey) {
- /* Zero the memory which previously held the public key */
- size_t key_len = strlen(c->publickey);
- git__memzero(c->publickey, key_len);
- git__free(c->publickey);
- }
-
- git__free(c);
-}
-
-static void ssh_interactive_free(struct git_cred *cred)
-{
- git_cred_ssh_interactive *c = (git_cred_ssh_interactive *)cred;
-
- git__free(c->username);
-
- git__free(c);
-}
-
-static void ssh_custom_free(struct git_cred *cred)
-{
- git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
-
- git__free(c->username);
-
- if (c->publickey) {
- /* Zero the memory which previously held the publickey */
- size_t key_len = strlen(c->publickey);
- git__memzero(c->publickey, key_len);
- git__free(c->publickey);
- }
-
- git__free(c);
-}
-
-static void default_free(struct git_cred *cred)
-{
- git_cred_default *c = (git_cred_default *)cred;
-
- git__free(c);
-}
-
-static void username_free(struct git_cred *cred)
-{
- git__free(cred);
-}
-
-int git_cred_ssh_key_new(
- git_cred **cred,
- const char *username,
- const char *publickey,
- const char *privatekey,
- const char *passphrase)
-{
- return git_cred_ssh_key_type_new(
- cred,
- username,
- publickey,
- privatekey,
- passphrase,
- GIT_CREDTYPE_SSH_KEY);
-}
-
-int git_cred_ssh_key_memory_new(
- git_cred **cred,
- const char *username,
- const char *publickey,
- const char *privatekey,
- const char *passphrase)
-{
-#ifdef GIT_SSH_MEMORY_CREDENTIALS
- return git_cred_ssh_key_type_new(
- cred,
- username,
- publickey,
- privatekey,
- passphrase,
- GIT_CREDTYPE_SSH_MEMORY);
-#else
- GIT_UNUSED(cred);
- GIT_UNUSED(username);
- GIT_UNUSED(publickey);
- GIT_UNUSED(privatekey);
- GIT_UNUSED(passphrase);
-
- giterr_set(GITERR_INVALID,
- "This version of libgit2 was not built with ssh memory credentials.");
- return -1;
-#endif
-}
-
-static int git_cred_ssh_key_type_new(
- git_cred **cred,
- const char *username,
- const char *publickey,
- const char *privatekey,
- const char *passphrase,
- git_credtype_t credtype)
-{
- git_cred_ssh_key *c;
-
- assert(username && cred && privatekey);
-
- c = git__calloc(1, sizeof(git_cred_ssh_key));
- GITERR_CHECK_ALLOC(c);
-
- c->parent.credtype = credtype;
- c->parent.free = ssh_key_free;
-
- c->username = git__strdup(username);
- GITERR_CHECK_ALLOC(c->username);
-
- c->privatekey = git__strdup(privatekey);
- GITERR_CHECK_ALLOC(c->privatekey);
-
- if (publickey) {
- c->publickey = git__strdup(publickey);
- GITERR_CHECK_ALLOC(c->publickey);
- }
-
- if (passphrase) {
- c->passphrase = git__strdup(passphrase);
- GITERR_CHECK_ALLOC(c->passphrase);
- }
-
- *cred = &c->parent;
- return 0;
-}
-
-int git_cred_ssh_interactive_new(
- git_cred **out,
- const char *username,
- git_cred_ssh_interactive_callback prompt_callback,
- void *payload)
-{
- git_cred_ssh_interactive *c;
-
- assert(out && username && prompt_callback);
-
- c = git__calloc(1, sizeof(git_cred_ssh_interactive));
- GITERR_CHECK_ALLOC(c);
-
- c->parent.credtype = GIT_CREDTYPE_SSH_INTERACTIVE;
- c->parent.free = ssh_interactive_free;
-
- c->username = git__strdup(username);
- GITERR_CHECK_ALLOC(c->username);
-
- c->prompt_callback = prompt_callback;
- c->payload = payload;
-
- *out = &c->parent;
- return 0;
-}
-
-int git_cred_ssh_key_from_agent(git_cred **cred, const char *username) {
- git_cred_ssh_key *c;
-
- assert(username && cred);
-
- c = git__calloc(1, sizeof(git_cred_ssh_key));
- GITERR_CHECK_ALLOC(c);
-
- c->parent.credtype = GIT_CREDTYPE_SSH_KEY;
- c->parent.free = ssh_key_free;
-
- c->username = git__strdup(username);
- GITERR_CHECK_ALLOC(c->username);
-
- c->privatekey = NULL;
-
- *cred = &c->parent;
- return 0;
-}
-
-int git_cred_ssh_custom_new(
- git_cred **cred,
- const char *username,
- const char *publickey,
- size_t publickey_len,
- git_cred_sign_callback sign_callback,
- void *payload)
-{
- git_cred_ssh_custom *c;
-
- assert(username && cred);
-
- c = git__calloc(1, sizeof(git_cred_ssh_custom));
- GITERR_CHECK_ALLOC(c);
-
- c->parent.credtype = GIT_CREDTYPE_SSH_CUSTOM;
- c->parent.free = ssh_custom_free;
-
- c->username = git__strdup(username);
- GITERR_CHECK_ALLOC(c->username);
-
- if (publickey_len > 0) {
- c->publickey = git__malloc(publickey_len);
- GITERR_CHECK_ALLOC(c->publickey);
-
- memcpy(c->publickey, publickey, publickey_len);
- }
-
- c->publickey_len = publickey_len;
- c->sign_callback = sign_callback;
- c->payload = payload;
-
- *cred = &c->parent;
- return 0;
-}
-
-int git_cred_default_new(git_cred **cred)
-{
- git_cred_default *c;
-
- assert(cred);
-
- c = git__calloc(1, sizeof(git_cred_default));
- GITERR_CHECK_ALLOC(c);
-
- c->credtype = GIT_CREDTYPE_DEFAULT;
- c->free = default_free;
-
- *cred = c;
- return 0;
-}
-
-int git_cred_username_new(git_cred **cred, const char *username)
-{
- git_cred_username *c;
- size_t len, allocsize;
-
- assert(cred);
-
- len = strlen(username);
-
- GITERR_CHECK_ALLOC_ADD(&allocsize, sizeof(git_cred_username), len);
- GITERR_CHECK_ALLOC_ADD(&allocsize, allocsize, 1);
- c = git__malloc(allocsize);
- GITERR_CHECK_ALLOC(c);
-
- c->parent.credtype = GIT_CREDTYPE_USERNAME;
- c->parent.free = username_free;
- memcpy(c->username, username, len + 1);
-
- *cred = (git_cred *) c;
- return 0;
-}
-
-void git_cred_free(git_cred *cred)
-{
- if (!cred)
- return;
-
- cred->free(cred);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_cred_h__
-#define INCLUDE_git_cred_h__
-
-#include "git2/transport.h"
-
-const char *git_cred__username(git_cred *cred);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "git2/cred_helpers.h"
-
-int git_cred_userpass(
- git_cred **cred,
- const char *url,
- const char *user_from_url,
- unsigned int allowed_types,
- void *payload)
-{
- git_cred_userpass_payload *userpass = (git_cred_userpass_payload*)payload;
- const char *effective_username = NULL;
-
- GIT_UNUSED(url);
-
- if (!userpass || !userpass->password) return -1;
-
- /* Username resolution: a username can be passed with the URL, the
- * credentials payload, or both. Here's what we do. Note that if we get
- * this far, we know that any password the url may contain has already
- * failed at least once, so we ignore it.
- *
- * | Payload | URL | Used |
- * +-------------+----------+-----------+
- * | yes | no | payload |
- * | yes | yes | payload |
- * | no | yes | url |
- * | no | no | FAIL |
- */
- if (userpass->username)
- effective_username = userpass->username;
- else if (user_from_url)
- effective_username = user_from_url;
- else
- return -1;
-
- if (GIT_CREDTYPE_USERNAME & allowed_types)
- return git_cred_username_new(cred, effective_username);
-
- if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 ||
- git_cred_userpass_plaintext_new(cred, effective_username, userpass->password) < 0)
- return -1;
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2.h"
-#include "buffer.h"
-#include "netops.h"
-#include "git2/sys/transport.h"
-#include "stream.h"
-#include "socket_stream.h"
-
-#define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
-
-static const char prefix_git[] = "git://";
-static const char cmd_uploadpack[] = "git-upload-pack";
-static const char cmd_receivepack[] = "git-receive-pack";
-
-typedef struct {
- git_smart_subtransport_stream parent;
- git_stream *io;
- const char *cmd;
- char *url;
- unsigned sent_command : 1;
-} git_proto_stream;
-
-typedef struct {
- git_smart_subtransport parent;
- git_transport *owner;
- git_proto_stream *current_stream;
-} git_subtransport;
-
-/*
- * Create a git protocol request.
- *
- * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
- */
-static int gen_proto(git_buf *request, const char *cmd, const char *url)
-{
- char *delim, *repo;
- char host[] = "host=";
- size_t len;
-
- delim = strchr(url, '/');
- if (delim == NULL) {
- giterr_set(GITERR_NET, "Malformed URL");
- return -1;
- }
-
- repo = delim;
- if (repo[1] == '~')
- ++repo;
-
- delim = strchr(url, ':');
- if (delim == NULL)
- delim = strchr(url, '/');
-
- len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
-
- git_buf_grow(request, len);
- git_buf_printf(request, "%04x%s %s%c%s",
- (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);
- git_buf_put(request, url, delim - url);
- git_buf_putc(request, '\0');
-
- if (git_buf_oom(request))
- return -1;
-
- return 0;
-}
-
-static int send_command(git_proto_stream *s)
-{
- int error;
- git_buf request = GIT_BUF_INIT;
-
- error = gen_proto(&request, s->cmd, s->url);
- if (error < 0)
- goto cleanup;
-
- error = git_stream_write(s->io, request.ptr, request.size, 0);
- if (error >= 0)
- s->sent_command = 1;
-
-cleanup:
- git_buf_free(&request);
- return error;
-}
-
-static int git_proto_stream_read(
- git_smart_subtransport_stream *stream,
- char *buffer,
- size_t buf_size,
- size_t *bytes_read)
-{
- int error;
- git_proto_stream *s = (git_proto_stream *)stream;
- gitno_buffer buf;
-
- *bytes_read = 0;
-
- if (!s->sent_command && (error = send_command(s)) < 0)
- return error;
-
- gitno_buffer_setup_fromstream(s->io, &buf, buffer, buf_size);
-
- if ((error = gitno_recv(&buf)) < 0)
- return error;
-
- *bytes_read = buf.offset;
-
- return 0;
-}
-
-static int git_proto_stream_write(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len)
-{
- int error;
- git_proto_stream *s = (git_proto_stream *)stream;
-
- if (!s->sent_command && (error = send_command(s)) < 0)
- return error;
-
- return git_stream_write(s->io, buffer, len, 0);
-}
-
-static void git_proto_stream_free(git_smart_subtransport_stream *stream)
-{
- git_proto_stream *s;
- git_subtransport *t;
-
- if (!stream)
- return;
-
- s = (git_proto_stream *)stream;
- t = OWNING_SUBTRANSPORT(s);
-
- t->current_stream = NULL;
-
- git_stream_close(s->io);
- git_stream_free(s->io);
- git__free(s->url);
- git__free(s);
-}
-
-static int git_proto_stream_alloc(
- git_subtransport *t,
- const char *url,
- const char *cmd,
- const char *host,
- const char *port,
- git_smart_subtransport_stream **stream)
-{
- git_proto_stream *s;
-
- if (!stream)
- return -1;
-
- s = git__calloc(1, sizeof(git_proto_stream));
- GITERR_CHECK_ALLOC(s);
-
- s->parent.subtransport = &t->parent;
- s->parent.read = git_proto_stream_read;
- s->parent.write = git_proto_stream_write;
- s->parent.free = git_proto_stream_free;
-
- s->cmd = cmd;
- s->url = git__strdup(url);
-
- if (!s->url) {
- git__free(s);
- return -1;
- }
-
- if ((git_socket_stream_new(&s->io, host, port)) < 0)
- return -1;
-
- GITERR_CHECK_VERSION(s->io, GIT_STREAM_VERSION, "git_stream");
-
- *stream = &s->parent;
- return 0;
-}
-
-static int _git_uploadpack_ls(
- git_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
- const char *stream_url = url;
- git_proto_stream *s;
- int error;
-
- *stream = NULL;
-
- if (!git__prefixcmp(url, prefix_git))
- stream_url += strlen(prefix_git);
-
- if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0)
- return error;
-
- error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream);
-
- git__free(host);
- git__free(port);
- git__free(path);
- git__free(user);
- git__free(pass);
-
-
- if (error < 0) {
- git_proto_stream_free(*stream);
- return error;
- }
-
- s = (git_proto_stream *) *stream;
- if ((error = git_stream_connect(s->io)) < 0) {
- git_proto_stream_free(*stream);
- return error;
- }
-
- t->current_stream = s;
-
- return 0;
-}
-
-static int _git_uploadpack(
- git_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- GIT_UNUSED(url);
-
- if (t->current_stream) {
- *stream = &t->current_stream->parent;
- return 0;
- }
-
- giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK");
- return -1;
-}
-
-static int _git_receivepack_ls(
- git_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
- const char *stream_url = url;
- git_proto_stream *s;
- int error;
-
- *stream = NULL;
- if (!git__prefixcmp(url, prefix_git))
- stream_url += strlen(prefix_git);
-
- if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0)
- return error;
-
- error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, host, port, stream);
-
- git__free(host);
- git__free(port);
- git__free(path);
- git__free(user);
- git__free(pass);
-
- if (error < 0) {
- git_proto_stream_free(*stream);
- return error;
- }
-
- s = (git_proto_stream *) *stream;
-
- if ((error = git_stream_connect(s->io)) < 0)
- return error;
-
- t->current_stream = s;
-
- return 0;
-}
-
-static int _git_receivepack(
- git_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- GIT_UNUSED(url);
-
- if (t->current_stream) {
- *stream = &t->current_stream->parent;
- return 0;
- }
-
- giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK");
- return -1;
-}
-
-static int _git_action(
- git_smart_subtransport_stream **stream,
- git_smart_subtransport *subtransport,
- const char *url,
- git_smart_service_t action)
-{
- git_subtransport *t = (git_subtransport *) subtransport;
-
- switch (action) {
- case GIT_SERVICE_UPLOADPACK_LS:
- return _git_uploadpack_ls(t, url, stream);
-
- case GIT_SERVICE_UPLOADPACK:
- return _git_uploadpack(t, url, stream);
-
- case GIT_SERVICE_RECEIVEPACK_LS:
- return _git_receivepack_ls(t, url, stream);
-
- case GIT_SERVICE_RECEIVEPACK:
- return _git_receivepack(t, url, stream);
- }
-
- *stream = NULL;
- return -1;
-}
-
-static int _git_close(git_smart_subtransport *subtransport)
-{
- git_subtransport *t = (git_subtransport *) subtransport;
-
- assert(!t->current_stream);
-
- GIT_UNUSED(t);
-
- return 0;
-}
-
-static void _git_free(git_smart_subtransport *subtransport)
-{
- git_subtransport *t = (git_subtransport *) subtransport;
-
- assert(!t->current_stream);
-
- git__free(t);
-}
-
-int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owner, void *param)
-{
- git_subtransport *t;
-
- GIT_UNUSED(param);
-
- if (!out)
- return -1;
-
- t = git__calloc(1, sizeof(git_subtransport));
- GITERR_CHECK_ALLOC(t);
-
- t->owner = owner;
- t->parent.action = _git_action;
- t->parent.close = _git_close;
- t->parent.free = _git_free;
-
- *out = (git_smart_subtransport *) t;
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef GIT_WINHTTP
-
-#include "git2.h"
-#include "http_parser.h"
-#include "buffer.h"
-#include "netops.h"
-#include "global.h"
-#include "remote.h"
-#include "smart.h"
-#include "auth.h"
-#include "auth_negotiate.h"
-#include "tls_stream.h"
-#include "socket_stream.h"
-#include "curl_stream.h"
-
-git_http_auth_scheme auth_schemes[] = {
- { GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
- { GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic },
-};
-
-static const char *upload_pack_service = "upload-pack";
-static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
-static const char *upload_pack_service_url = "/git-upload-pack";
-static const char *receive_pack_service = "receive-pack";
-static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack";
-static const char *receive_pack_service_url = "/git-receive-pack";
-static const char *get_verb = "GET";
-static const char *post_verb = "POST";
-
-#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
-
-#define PARSE_ERROR_GENERIC -1
-#define PARSE_ERROR_REPLAY -2
-/** Look at the user field */
-#define PARSE_ERROR_EXT -3
-
-#define CHUNK_SIZE 4096
-
-enum last_cb {
- NONE,
- FIELD,
- VALUE
-};
-
-typedef struct {
- git_smart_subtransport_stream parent;
- const char *service;
- const char *service_url;
- char *redirect_url;
- const char *verb;
- char *chunk_buffer;
- unsigned chunk_buffer_len;
- unsigned sent_request : 1,
- received_response : 1,
- chunked : 1,
- redirect_count : 3;
-} http_stream;
-
-typedef struct {
- git_smart_subtransport parent;
- transport_smart *owner;
- git_stream *io;
- gitno_connection_data connection_data;
- bool connected;
-
- /* Parser structures */
- http_parser parser;
- http_parser_settings settings;
- gitno_buffer parse_buffer;
- git_buf parse_header_name;
- git_buf parse_header_value;
- char parse_buffer_data[NETIO_BUFSIZE];
- char *content_type;
- char *location;
- git_vector www_authenticate;
- enum last_cb last_cb;
- int parse_error;
- int error;
- unsigned parse_finished : 1;
-
- /* Authentication */
- git_cred *cred;
- git_cred *url_cred;
- git_vector auth_contexts;
-} http_subtransport;
-
-typedef struct {
- http_stream *s;
- http_subtransport *t;
-
- /* Target buffer details from read() */
- char *buffer;
- size_t buf_size;
- size_t *bytes_read;
-} parser_context;
-
-static bool credtype_match(git_http_auth_scheme *scheme, void *data)
-{
- unsigned int credtype = *(unsigned int *)data;
-
- return !!(scheme->credtypes & credtype);
-}
-
-static bool challenge_match(git_http_auth_scheme *scheme, void *data)
-{
- const char *scheme_name = scheme->name;
- const char *challenge = (const char *)data;
- size_t scheme_len;
-
- scheme_len = strlen(scheme_name);
- return (strncasecmp(challenge, scheme_name, scheme_len) == 0 &&
- (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '));
-}
-
-static int auth_context_match(
- git_http_auth_context **out,
- http_subtransport *t,
- bool (*scheme_match)(git_http_auth_scheme *scheme, void *data),
- void *data)
-{
- git_http_auth_scheme *scheme = NULL;
- git_http_auth_context *context = NULL, *c;
- size_t i;
-
- *out = NULL;
-
- for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
- if (scheme_match(&auth_schemes[i], data)) {
- scheme = &auth_schemes[i];
- break;
- }
- }
-
- if (!scheme)
- return 0;
-
- /* See if authentication has already started for this scheme */
- git_vector_foreach(&t->auth_contexts, i, c) {
- if (c->type == scheme->type) {
- context = c;
- break;
- }
- }
-
- if (!context) {
- if (scheme->init_context(&context, &t->connection_data) < 0)
- return -1;
- else if (!context)
- return 0;
- else if (git_vector_insert(&t->auth_contexts, context) < 0)
- return -1;
- }
-
- *out = context;
-
- return 0;
-}
-
-static int apply_credentials(git_buf *buf, http_subtransport *t)
-{
- git_cred *cred = t->cred;
- git_http_auth_context *context;
-
- /* Apply the credentials given to us in the URL */
- if (!cred && t->connection_data.user && t->connection_data.pass) {
- if (!t->url_cred &&
- git_cred_userpass_plaintext_new(&t->url_cred,
- t->connection_data.user, t->connection_data.pass) < 0)
- return -1;
-
- cred = t->url_cred;
- }
-
- if (!cred)
- return 0;
-
- /* Get or create a context for the best scheme for this cred type */
- if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0)
- return -1;
-
- return context->next_token(buf, context, cred);
-}
-
-static const char *user_agent(void)
-{
- const char *custom = git_libgit2__user_agent();
-
- if (custom)
- return custom;
-
- return "libgit2 " LIBGIT2_VERSION;
-}
-
-static int gen_request(
- git_buf *buf,
- http_stream *s,
- size_t content_length)
-{
- http_subtransport *t = OWNING_SUBTRANSPORT(s);
- const char *path = t->connection_data.path ? t->connection_data.path : "/";
- size_t i;
-
- git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
-
- git_buf_printf(buf, "User-Agent: git/2.0 (%s)\r\n", user_agent());
- git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
-
- if (s->chunked || content_length > 0) {
- git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
- git_buf_printf(buf, "Content-Type: application/x-git-%s-request\r\n", s->service);
-
- if (s->chunked)
- git_buf_puts(buf, "Transfer-Encoding: chunked\r\n");
- else
- git_buf_printf(buf, "Content-Length: %"PRIuZ "\r\n", content_length);
- } else
- git_buf_puts(buf, "Accept: */*\r\n");
-
- for (i = 0; i < t->owner->custom_headers.count; i++) {
- if (t->owner->custom_headers.strings[i])
- git_buf_printf(buf, "%s\r\n", t->owner->custom_headers.strings[i]);
- }
-
- /* Apply credentials to the request */
- if (apply_credentials(buf, t) < 0)
- return -1;
-
- git_buf_puts(buf, "\r\n");
-
- if (git_buf_oom(buf))
- return -1;
-
- return 0;
-}
-
-static int parse_authenticate_response(
- git_vector *www_authenticate,
- http_subtransport *t,
- int *allowed_types)
-{
- git_http_auth_context *context;
- char *challenge;
- size_t i;
-
- git_vector_foreach(www_authenticate, i, challenge) {
- if (auth_context_match(&context, t, challenge_match, challenge) < 0)
- return -1;
- else if (!context)
- continue;
-
- if (context->set_challenge &&
- context->set_challenge(context, challenge) < 0)
- return -1;
-
- *allowed_types |= context->credtypes;
- }
-
- return 0;
-}
-
-static int on_header_ready(http_subtransport *t)
-{
- git_buf *name = &t->parse_header_name;
- git_buf *value = &t->parse_header_value;
-
- if (!strcasecmp("Content-Type", git_buf_cstr(name))) {
- if (!t->content_type) {
- t->content_type = git__strdup(git_buf_cstr(value));
- GITERR_CHECK_ALLOC(t->content_type);
- }
- }
- else if (!strcasecmp("WWW-Authenticate", git_buf_cstr(name))) {
- char *dup = git__strdup(git_buf_cstr(value));
- GITERR_CHECK_ALLOC(dup);
-
- git_vector_insert(&t->www_authenticate, dup);
- }
- else if (!strcasecmp("Location", git_buf_cstr(name))) {
- if (!t->location) {
- t->location = git__strdup(git_buf_cstr(value));
- GITERR_CHECK_ALLOC(t->location);
- }
- }
-
- return 0;
-}
-
-static int on_header_field(http_parser *parser, const char *str, size_t len)
-{
- parser_context *ctx = (parser_context *) parser->data;
- http_subtransport *t = ctx->t;
-
- /* Both parse_header_name and parse_header_value are populated
- * and ready for consumption */
- if (VALUE == t->last_cb)
- if (on_header_ready(t) < 0)
- return t->parse_error = PARSE_ERROR_GENERIC;
-
- if (NONE == t->last_cb || VALUE == t->last_cb)
- git_buf_clear(&t->parse_header_name);
-
- if (git_buf_put(&t->parse_header_name, str, len) < 0)
- return t->parse_error = PARSE_ERROR_GENERIC;
-
- t->last_cb = FIELD;
- return 0;
-}
-
-static int on_header_value(http_parser *parser, const char *str, size_t len)
-{
- parser_context *ctx = (parser_context *) parser->data;
- http_subtransport *t = ctx->t;
-
- assert(NONE != t->last_cb);
-
- if (FIELD == t->last_cb)
- git_buf_clear(&t->parse_header_value);
-
- if (git_buf_put(&t->parse_header_value, str, len) < 0)
- return t->parse_error = PARSE_ERROR_GENERIC;
-
- t->last_cb = VALUE;
- return 0;
-}
-
-static int on_headers_complete(http_parser *parser)
-{
- parser_context *ctx = (parser_context *) parser->data;
- http_subtransport *t = ctx->t;
- http_stream *s = ctx->s;
- git_buf buf = GIT_BUF_INIT;
- int error = 0, no_callback = 0, allowed_auth_types = 0;
-
- /* Both parse_header_name and parse_header_value are populated
- * and ready for consumption. */
- if (VALUE == t->last_cb)
- if (on_header_ready(t) < 0)
- return t->parse_error = PARSE_ERROR_GENERIC;
-
- /* Capture authentication headers which may be a 401 (authentication
- * is not complete) or a 200 (simply informing us that auth *is*
- * complete.)
- */
- if (parse_authenticate_response(&t->www_authenticate, t,
- &allowed_auth_types) < 0)
- return t->parse_error = PARSE_ERROR_GENERIC;
-
- /* Check for an authentication failure. */
- if (parser->status_code == 401 && get_verb == s->verb) {
- if (!t->owner->cred_acquire_cb) {
- no_callback = 1;
- } else {
- if (allowed_auth_types) {
- if (t->cred) {
- t->cred->free(t->cred);
- t->cred = NULL;
- }
-
- error = t->owner->cred_acquire_cb(&t->cred,
- t->owner->url,
- t->connection_data.user,
- allowed_auth_types,
- t->owner->cred_acquire_payload);
-
- if (error == GIT_PASSTHROUGH) {
- no_callback = 1;
- } else if (error < 0) {
- t->error = error;
- return t->parse_error = PARSE_ERROR_EXT;
- } else {
- assert(t->cred);
-
- if (!(t->cred->credtype & allowed_auth_types)) {
- giterr_set(GITERR_NET, "credentials callback returned an invalid cred type");
- return t->parse_error = PARSE_ERROR_GENERIC;
- }
-
- /* Successfully acquired a credential. */
- t->parse_error = PARSE_ERROR_REPLAY;
- return 0;
- }
- }
- }
-
- if (no_callback) {
- giterr_set(GITERR_NET, "authentication required but no callback set");
- return t->parse_error = PARSE_ERROR_GENERIC;
- }
- }
-
- /* Check for a redirect.
- * Right now we only permit a redirect to the same hostname. */
- if ((parser->status_code == 301 ||
- parser->status_code == 302 ||
- (parser->status_code == 303 && get_verb == s->verb) ||
- parser->status_code == 307) &&
- t->location) {
-
- if (s->redirect_count >= 7) {
- giterr_set(GITERR_NET, "Too many redirects");
- return t->parse_error = PARSE_ERROR_GENERIC;
- }
-
- if (gitno_connection_data_from_url(&t->connection_data, t->location, s->service_url) < 0)
- return t->parse_error = PARSE_ERROR_GENERIC;
-
- /* Set the redirect URL on the stream. This is a transfer of
- * ownership of the memory. */
- if (s->redirect_url)
- git__free(s->redirect_url);
-
- s->redirect_url = t->location;
- t->location = NULL;
-
- t->connected = 0;
- s->redirect_count++;
-
- t->parse_error = PARSE_ERROR_REPLAY;
- return 0;
- }
-
- /* Check for a 200 HTTP status code. */
- if (parser->status_code != 200) {
- giterr_set(GITERR_NET,
- "Unexpected HTTP status code: %d",
- parser->status_code);
- return t->parse_error = PARSE_ERROR_GENERIC;
- }
-
- /* The response must contain a Content-Type header. */
- if (!t->content_type) {
- giterr_set(GITERR_NET, "No Content-Type header in response");
- return t->parse_error = PARSE_ERROR_GENERIC;
- }
-
- /* The Content-Type header must match our expectation. */
- if (get_verb == s->verb)
- git_buf_printf(&buf,
- "application/x-git-%s-advertisement",
- ctx->s->service);
- else
- git_buf_printf(&buf,
- "application/x-git-%s-result",
- ctx->s->service);
-
- if (git_buf_oom(&buf))
- return t->parse_error = PARSE_ERROR_GENERIC;
-
- if (strcmp(t->content_type, git_buf_cstr(&buf))) {
- git_buf_free(&buf);
- giterr_set(GITERR_NET,
- "Invalid Content-Type: %s",
- t->content_type);
- return t->parse_error = PARSE_ERROR_GENERIC;
- }
-
- git_buf_free(&buf);
-
- return 0;
-}
-
-static int on_message_complete(http_parser *parser)
-{
- parser_context *ctx = (parser_context *) parser->data;
- http_subtransport *t = ctx->t;
-
- t->parse_finished = 1;
-
- return 0;
-}
-
-static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
-{
- parser_context *ctx = (parser_context *) parser->data;
- http_subtransport *t = ctx->t;
-
- /* If our goal is to replay the request (either an auth failure or
- * a redirect) then don't bother buffering since we're ignoring the
- * content anyway.
- */
- if (t->parse_error == PARSE_ERROR_REPLAY)
- return 0;
-
- if (ctx->buf_size < len) {
- giterr_set(GITERR_NET, "Can't fit data in the buffer");
- return t->parse_error = PARSE_ERROR_GENERIC;
- }
-
- memcpy(ctx->buffer, str, len);
- *(ctx->bytes_read) += len;
- ctx->buffer += len;
- ctx->buf_size -= len;
-
- return 0;
-}
-
-static void clear_parser_state(http_subtransport *t)
-{
- http_parser_init(&t->parser, HTTP_RESPONSE);
- gitno_buffer_setup_fromstream(t->io,
- &t->parse_buffer,
- t->parse_buffer_data,
- sizeof(t->parse_buffer_data));
-
- t->last_cb = NONE;
- t->parse_error = 0;
- t->parse_finished = 0;
-
- git_buf_free(&t->parse_header_name);
- git_buf_init(&t->parse_header_name, 0);
-
- git_buf_free(&t->parse_header_value);
- git_buf_init(&t->parse_header_value, 0);
-
- git__free(t->content_type);
- t->content_type = NULL;
-
- git__free(t->location);
- t->location = NULL;
-
- git_vector_free_deep(&t->www_authenticate);
-}
-
-static int write_chunk(git_stream *io, const char *buffer, size_t len)
-{
- git_buf buf = GIT_BUF_INIT;
-
- /* Chunk header */
- git_buf_printf(&buf, "%" PRIxZ "\r\n", len);
-
- if (git_buf_oom(&buf))
- return -1;
-
- if (git_stream_write(io, buf.ptr, buf.size, 0) < 0) {
- git_buf_free(&buf);
- return -1;
- }
-
- git_buf_free(&buf);
-
- /* Chunk body */
- if (len > 0 && git_stream_write(io, buffer, len, 0) < 0)
- return -1;
-
- /* Chunk footer */
- if (git_stream_write(io, "\r\n", 2, 0) < 0)
- return -1;
-
- return 0;
-}
-
-static int apply_proxy_config(http_subtransport *t)
-{
- int error;
- git_proxy_t proxy_type;
-
- if (!git_stream_supports_proxy(t->io))
- return 0;
-
- proxy_type = t->owner->proxy.type;
-
- if (proxy_type == GIT_PROXY_NONE)
- return 0;
-
- if (proxy_type == GIT_PROXY_AUTO) {
- char *url;
- git_proxy_options opts = GIT_PROXY_OPTIONS_INIT;
-
- if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &url)) < 0)
- return error;
-
- opts.type = GIT_PROXY_SPECIFIED;
- opts.url = url;
- error = git_stream_set_proxy(t->io, &opts);
- git__free(url);
-
- return error;
- }
-
- return git_stream_set_proxy(t->io, &t->owner->proxy);
-}
-
-static int http_connect(http_subtransport *t)
-{
- int error;
-
- if (t->connected &&
- http_should_keep_alive(&t->parser) &&
- t->parse_finished)
- return 0;
-
- if (t->io) {
- git_stream_close(t->io);
- git_stream_free(t->io);
- t->io = NULL;
- t->connected = 0;
- }
-
- if (t->connection_data.use_ssl) {
- error = git_tls_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
- } else {
-#ifdef GIT_CURL
- error = git_curl_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
-#else
- error = git_socket_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
-#endif
- }
-
- if (error < 0)
- return error;
-
- GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream");
-
- apply_proxy_config(t);
-
- error = git_stream_connect(t->io);
-
- if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL &&
- git_stream_is_encrypted(t->io)) {
- git_cert *cert;
- int is_valid;
-
- if ((error = git_stream_certificate(&cert, t->io)) < 0)
- return error;
-
- giterr_clear();
- is_valid = error != GIT_ECERTIFICATE;
- error = t->owner->certificate_check_cb(cert, is_valid, t->connection_data.host, t->owner->message_cb_payload);
-
- if (error < 0) {
- if (!giterr_last())
- giterr_set(GITERR_NET, "user cancelled certificate check");
-
- return error;
- }
- }
-
- if (error < 0)
- return error;
-
- t->connected = 1;
- return 0;
-}
-
-static int http_stream_read(
- git_smart_subtransport_stream *stream,
- char *buffer,
- size_t buf_size,
- size_t *bytes_read)
-{
- http_stream *s = (http_stream *)stream;
- http_subtransport *t = OWNING_SUBTRANSPORT(s);
- parser_context ctx;
- size_t bytes_parsed;
-
-replay:
- *bytes_read = 0;
-
- assert(t->connected);
-
- if (!s->sent_request) {
- git_buf request = GIT_BUF_INIT;
-
- clear_parser_state(t);
-
- if (gen_request(&request, s, 0) < 0)
- return -1;
-
- if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) {
- git_buf_free(&request);
- return -1;
- }
-
- git_buf_free(&request);
-
- s->sent_request = 1;
- }
-
- if (!s->received_response) {
- if (s->chunked) {
- assert(s->verb == post_verb);
-
- /* Flush, if necessary */
- if (s->chunk_buffer_len > 0 &&
- write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
- return -1;
-
- s->chunk_buffer_len = 0;
-
- /* Write the final chunk. */
- if (git_stream_write(t->io, "0\r\n\r\n", 5, 0) < 0)
- return -1;
- }
-
- s->received_response = 1;
- }
-
- while (!*bytes_read && !t->parse_finished) {
- size_t data_offset;
- int error;
-
- /*
- * Make the parse_buffer think it's as full of data as
- * the buffer, so it won't try to recv more data than
- * we can put into it.
- *
- * data_offset is the actual data offset from which we
- * should tell the parser to start reading.
- */
- if (buf_size >= t->parse_buffer.len) {
- t->parse_buffer.offset = 0;
- } else {
- t->parse_buffer.offset = t->parse_buffer.len - buf_size;
- }
-
- data_offset = t->parse_buffer.offset;
-
- if (gitno_recv(&t->parse_buffer) < 0)
- return -1;
-
- /* This call to http_parser_execute will result in invocations of the
- * on_* family of callbacks. The most interesting of these is
- * on_body_fill_buffer, which is called when data is ready to be copied
- * into the target buffer. We need to marshal the buffer, buf_size, and
- * bytes_read parameters to this callback. */
- ctx.t = t;
- ctx.s = s;
- ctx.buffer = buffer;
- ctx.buf_size = buf_size;
- ctx.bytes_read = bytes_read;
-
- /* Set the context, call the parser, then unset the context. */
- t->parser.data = &ctx;
-
- bytes_parsed = http_parser_execute(&t->parser,
- &t->settings,
- t->parse_buffer.data + data_offset,
- t->parse_buffer.offset - data_offset);
-
- t->parser.data = NULL;
-
- /* If there was a handled authentication failure, then parse_error
- * will have signaled us that we should replay the request. */
- if (PARSE_ERROR_REPLAY == t->parse_error) {
- s->sent_request = 0;
-
- if ((error = http_connect(t)) < 0)
- return error;
-
- goto replay;
- }
-
- if (t->parse_error == PARSE_ERROR_EXT) {
- return t->error;
- }
-
- if (t->parse_error < 0)
- return -1;
-
- if (bytes_parsed != t->parse_buffer.offset - data_offset) {
- giterr_set(GITERR_NET,
- "HTTP parser error: %s",
- http_errno_description((enum http_errno)t->parser.http_errno));
- return -1;
- }
- }
-
- return 0;
-}
-
-static int http_stream_write_chunked(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len)
-{
- http_stream *s = (http_stream *)stream;
- http_subtransport *t = OWNING_SUBTRANSPORT(s);
-
- assert(t->connected);
-
- /* Send the request, if necessary */
- if (!s->sent_request) {
- git_buf request = GIT_BUF_INIT;
-
- clear_parser_state(t);
-
- if (gen_request(&request, s, 0) < 0)
- return -1;
-
- if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) {
- git_buf_free(&request);
- return -1;
- }
-
- git_buf_free(&request);
-
- s->sent_request = 1;
- }
-
- if (len > CHUNK_SIZE) {
- /* Flush, if necessary */
- if (s->chunk_buffer_len > 0) {
- if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
- return -1;
-
- s->chunk_buffer_len = 0;
- }
-
- /* Write chunk directly */
- if (write_chunk(t->io, buffer, len) < 0)
- return -1;
- }
- else {
- /* Append as much to the buffer as we can */
- int count = min(CHUNK_SIZE - s->chunk_buffer_len, len);
-
- if (!s->chunk_buffer)
- s->chunk_buffer = git__malloc(CHUNK_SIZE);
-
- memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
- s->chunk_buffer_len += count;
- buffer += count;
- len -= count;
-
- /* Is the buffer full? If so, then flush */
- if (CHUNK_SIZE == s->chunk_buffer_len) {
- if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0)
- return -1;
-
- s->chunk_buffer_len = 0;
-
- if (len > 0) {
- memcpy(s->chunk_buffer, buffer, len);
- s->chunk_buffer_len = len;
- }
- }
- }
-
- return 0;
-}
-
-static int http_stream_write_single(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len)
-{
- http_stream *s = (http_stream *)stream;
- http_subtransport *t = OWNING_SUBTRANSPORT(s);
- git_buf request = GIT_BUF_INIT;
-
- assert(t->connected);
-
- if (s->sent_request) {
- giterr_set(GITERR_NET, "Subtransport configured for only one write");
- return -1;
- }
-
- clear_parser_state(t);
-
- if (gen_request(&request, s, len) < 0)
- return -1;
-
- if (git_stream_write(t->io, request.ptr, request.size, 0) < 0)
- goto on_error;
-
- if (len && git_stream_write(t->io, buffer, len, 0) < 0)
- goto on_error;
-
- git_buf_free(&request);
- s->sent_request = 1;
-
- return 0;
-
-on_error:
- git_buf_free(&request);
- return -1;
-}
-
-static void http_stream_free(git_smart_subtransport_stream *stream)
-{
- http_stream *s = (http_stream *)stream;
-
- if (s->chunk_buffer)
- git__free(s->chunk_buffer);
-
- if (s->redirect_url)
- git__free(s->redirect_url);
-
- git__free(s);
-}
-
-static int http_stream_alloc(http_subtransport *t,
- git_smart_subtransport_stream **stream)
-{
- http_stream *s;
-
- if (!stream)
- return -1;
-
- s = git__calloc(sizeof(http_stream), 1);
- GITERR_CHECK_ALLOC(s);
-
- s->parent.subtransport = &t->parent;
- s->parent.read = http_stream_read;
- s->parent.write = http_stream_write_single;
- s->parent.free = http_stream_free;
-
- *stream = (git_smart_subtransport_stream *)s;
- return 0;
-}
-
-static int http_uploadpack_ls(
- http_subtransport *t,
- git_smart_subtransport_stream **stream)
-{
- http_stream *s;
-
- if (http_stream_alloc(t, stream) < 0)
- return -1;
-
- s = (http_stream *)*stream;
-
- s->service = upload_pack_service;
- s->service_url = upload_pack_ls_service_url;
- s->verb = get_verb;
-
- return 0;
-}
-
-static int http_uploadpack(
- http_subtransport *t,
- git_smart_subtransport_stream **stream)
-{
- http_stream *s;
-
- if (http_stream_alloc(t, stream) < 0)
- return -1;
-
- s = (http_stream *)*stream;
-
- s->service = upload_pack_service;
- s->service_url = upload_pack_service_url;
- s->verb = post_verb;
-
- return 0;
-}
-
-static int http_receivepack_ls(
- http_subtransport *t,
- git_smart_subtransport_stream **stream)
-{
- http_stream *s;
-
- if (http_stream_alloc(t, stream) < 0)
- return -1;
-
- s = (http_stream *)*stream;
-
- s->service = receive_pack_service;
- s->service_url = receive_pack_ls_service_url;
- s->verb = get_verb;
-
- return 0;
-}
-
-static int http_receivepack(
- http_subtransport *t,
- git_smart_subtransport_stream **stream)
-{
- http_stream *s;
-
- if (http_stream_alloc(t, stream) < 0)
- return -1;
-
- s = (http_stream *)*stream;
-
- /* Use Transfer-Encoding: chunked for this request */
- s->chunked = 1;
- s->parent.write = http_stream_write_chunked;
-
- s->service = receive_pack_service;
- s->service_url = receive_pack_service_url;
- s->verb = post_verb;
-
- return 0;
-}
-
-static int http_action(
- git_smart_subtransport_stream **stream,
- git_smart_subtransport *subtransport,
- const char *url,
- git_smart_service_t action)
-{
- http_subtransport *t = (http_subtransport *)subtransport;
- int ret;
-
- if (!stream)
- return -1;
-
- if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) &&
- (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
- return ret;
-
- if ((ret = http_connect(t)) < 0)
- return ret;
-
- switch (action) {
- case GIT_SERVICE_UPLOADPACK_LS:
- return http_uploadpack_ls(t, stream);
-
- case GIT_SERVICE_UPLOADPACK:
- return http_uploadpack(t, stream);
-
- case GIT_SERVICE_RECEIVEPACK_LS:
- return http_receivepack_ls(t, stream);
-
- case GIT_SERVICE_RECEIVEPACK:
- return http_receivepack(t, stream);
- }
-
- *stream = NULL;
- return -1;
-}
-
-static int http_close(git_smart_subtransport *subtransport)
-{
- http_subtransport *t = (http_subtransport *) subtransport;
- git_http_auth_context *context;
- size_t i;
-
- clear_parser_state(t);
-
- t->connected = 0;
-
- if (t->io) {
- git_stream_close(t->io);
- git_stream_free(t->io);
- t->io = NULL;
- }
-
- if (t->cred) {
- t->cred->free(t->cred);
- t->cred = NULL;
- }
-
- if (t->url_cred) {
- t->url_cred->free(t->url_cred);
- t->url_cred = NULL;
- }
-
- git_vector_foreach(&t->auth_contexts, i, context) {
- if (context->free)
- context->free(context);
- }
-
- git_vector_clear(&t->auth_contexts);
-
- gitno_connection_data_free_ptrs(&t->connection_data);
- memset(&t->connection_data, 0x0, sizeof(gitno_connection_data));
-
- return 0;
-}
-
-static void http_free(git_smart_subtransport *subtransport)
-{
- http_subtransport *t = (http_subtransport *) subtransport;
-
- http_close(subtransport);
-
- git_vector_free(&t->auth_contexts);
- git__free(t);
-}
-
-int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
-{
- http_subtransport *t;
-
- GIT_UNUSED(param);
-
- if (!out)
- return -1;
-
- t = git__calloc(sizeof(http_subtransport), 1);
- GITERR_CHECK_ALLOC(t);
-
- t->owner = (transport_smart *)owner;
- t->parent.action = http_action;
- t->parent.close = http_close;
- t->parent.free = http_free;
-
- t->settings.on_header_field = on_header_field;
- t->settings.on_header_value = on_header_value;
- t->settings.on_headers_complete = on_headers_complete;
- t->settings.on_body = on_body_fill_buffer;
- t->settings.on_message_complete = on_message_complete;
-
- *out = (git_smart_subtransport *) t;
- return 0;
-}
-
-#endif /* !GIT_WINHTTP */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "git2/types.h"
-#include "git2/net.h"
-#include "git2/repository.h"
-#include "git2/object.h"
-#include "git2/tag.h"
-#include "git2/transport.h"
-#include "git2/revwalk.h"
-#include "git2/odb_backend.h"
-#include "git2/pack.h"
-#include "git2/commit.h"
-#include "git2/revparse.h"
-#include "pack-objects.h"
-#include "refs.h"
-#include "posix.h"
-#include "path.h"
-#include "buffer.h"
-#include "repository.h"
-#include "odb.h"
-#include "push.h"
-#include "remote.h"
-#include "proxy.h"
-
-typedef struct {
- git_transport parent;
- git_remote *owner;
- char *url;
- int direction;
- int flags;
- git_atomic cancelled;
- git_repository *repo;
- git_transport_message_cb progress_cb;
- git_transport_message_cb error_cb;
- void *message_cb_payload;
- git_vector refs;
- unsigned connected : 1,
- have_refs : 1;
-} transport_local;
-
-static void free_head(git_remote_head *head)
-{
- git__free(head->name);
- git__free(head->symref_target);
- git__free(head);
-}
-
-static void free_heads(git_vector *heads)
-{
- git_remote_head *head;
- size_t i;
-
- git_vector_foreach(heads, i, head)
- free_head(head);
-
- git_vector_free(heads);
-}
-
-static int add_ref(transport_local *t, const char *name)
-{
- const char peeled[] = "^{}";
- git_reference *ref, *resolved;
- git_remote_head *head;
- git_oid obj_id;
- git_object *obj = NULL, *target = NULL;
- git_buf buf = GIT_BUF_INIT;
- int error;
-
- if ((error = git_reference_lookup(&ref, t->repo, name)) < 0)
- return error;
-
- error = git_reference_resolve(&resolved, ref);
- if (error < 0) {
- git_reference_free(ref);
- if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) {
- /* This is actually okay. Empty repos often have a HEAD that
- * points to a nonexistent "refs/heads/master". */
- giterr_clear();
- return 0;
- }
- return error;
- }
-
- git_oid_cpy(&obj_id, git_reference_target(resolved));
- git_reference_free(resolved);
-
- head = git__calloc(1, sizeof(git_remote_head));
- GITERR_CHECK_ALLOC(head);
-
- head->name = git__strdup(name);
- GITERR_CHECK_ALLOC(head->name);
-
- git_oid_cpy(&head->oid, &obj_id);
-
- if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
- head->symref_target = git__strdup(git_reference_symbolic_target(ref));
- GITERR_CHECK_ALLOC(head->symref_target);
- }
- git_reference_free(ref);
-
- if ((error = git_vector_insert(&t->refs, head)) < 0) {
- free_head(head);
- return error;
- }
-
- /* If it's not a tag, we don't need to try to peel it */
- if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
- return 0;
-
- if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY)) < 0)
- return error;
-
- head = NULL;
-
- /* If it's not an annotated tag, or if we're mocking
- * git-receive-pack, just get out */
- if (git_object_type(obj) != GIT_OBJ_TAG ||
- t->direction != GIT_DIRECTION_FETCH) {
- git_object_free(obj);
- return 0;
- }
-
- /* And if it's a tag, peel it, and add it to the list */
- head = git__calloc(1, sizeof(git_remote_head));
- GITERR_CHECK_ALLOC(head);
-
- if (git_buf_join(&buf, 0, name, peeled) < 0) {
- free_head(head);
- return -1;
- }
- head->name = git_buf_detach(&buf);
-
- if (!(error = git_tag_peel(&target, (git_tag *)obj))) {
- git_oid_cpy(&head->oid, git_object_id(target));
-
- if ((error = git_vector_insert(&t->refs, head)) < 0) {
- free_head(head);
- }
- }
-
- git_object_free(obj);
- git_object_free(target);
-
- return error;
-}
-
-static int store_refs(transport_local *t)
-{
- size_t i;
- git_remote_head *head;
- git_strarray ref_names = {0};
-
- assert(t);
-
- if (git_reference_list(&ref_names, t->repo) < 0)
- goto on_error;
-
- /* Clear all heads we might have fetched in a previous connect */
- git_vector_foreach(&t->refs, i, head) {
- git__free(head->name);
- git__free(head);
- }
-
- /* Clear the vector so we can reuse it */
- git_vector_clear(&t->refs);
-
- /* Sort the references first */
- git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
-
- /* Add HEAD iff direction is fetch */
- if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0)
- goto on_error;
-
- for (i = 0; i < ref_names.count; ++i) {
- if (add_ref(t, ref_names.strings[i]) < 0)
- goto on_error;
- }
-
- t->have_refs = 1;
- git_strarray_free(&ref_names);
- return 0;
-
-on_error:
- git_vector_free(&t->refs);
- git_strarray_free(&ref_names);
- return -1;
-}
-
-/*
- * Try to open the url as a git directory. The direction doesn't
- * matter in this case because we're calculating the heads ourselves.
- */
-static int local_connect(
- git_transport *transport,
- const char *url,
- git_cred_acquire_cb cred_acquire_cb,
- void *cred_acquire_payload,
- const git_proxy_options *proxy,
- int direction, int flags)
-{
- git_repository *repo;
- int error;
- transport_local *t = (transport_local *) transport;
- const char *path;
- git_buf buf = GIT_BUF_INIT;
-
- GIT_UNUSED(cred_acquire_cb);
- GIT_UNUSED(cred_acquire_payload);
- GIT_UNUSED(proxy);
-
- if (t->connected)
- return 0;
-
- free_heads(&t->refs);
-
- t->url = git__strdup(url);
- GITERR_CHECK_ALLOC(t->url);
- t->direction = direction;
- t->flags = flags;
-
- /* 'url' may be a url or path; convert to a path */
- if ((error = git_path_from_url_or_path(&buf, url)) < 0) {
- git_buf_free(&buf);
- return error;
- }
- path = git_buf_cstr(&buf);
-
- error = git_repository_open(&repo, path);
-
- git_buf_free(&buf);
-
- if (error < 0)
- return -1;
-
- t->repo = repo;
-
- if (store_refs(t) < 0)
- return -1;
-
- t->connected = 1;
-
- return 0;
-}
-
-static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
-{
- transport_local *t = (transport_local *)transport;
-
- if (!t->have_refs) {
- giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
- return -1;
- }
-
- *out = (const git_remote_head **)t->refs.contents;
- *size = t->refs.length;
-
- return 0;
-}
-
-static int local_negotiate_fetch(
- git_transport *transport,
- git_repository *repo,
- const git_remote_head * const *refs,
- size_t count)
-{
- transport_local *t = (transport_local*)transport;
- git_remote_head *rhead;
- unsigned int i;
-
- GIT_UNUSED(refs);
- GIT_UNUSED(count);
-
- /* Fill in the loids */
- git_vector_foreach(&t->refs, i, rhead) {
- git_object *obj;
-
- int error = git_revparse_single(&obj, repo, rhead->name);
- if (!error)
- git_oid_cpy(&rhead->loid, git_object_id(obj));
- else if (error != GIT_ENOTFOUND)
- return error;
- else
- giterr_clear();
- git_object_free(obj);
- }
-
- return 0;
-}
-
-static int local_push_update_remote_ref(
- git_repository *remote_repo,
- const char *lref,
- const char *rref,
- git_oid *loid,
- git_oid *roid)
-{
- int error;
- git_reference *remote_ref = NULL;
-
- /* check for lhs, if it's empty it means to delete */
- if (lref[0] != '\0') {
- /* Create or update a ref */
- error = git_reference_create(NULL, remote_repo, rref, loid,
- !git_oid_iszero(roid), NULL);
- } else {
- /* Delete a ref */
- if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) {
- if (error == GIT_ENOTFOUND)
- error = 0;
- return error;
- }
-
- error = git_reference_delete(remote_ref);
- git_reference_free(remote_ref);
- }
-
- return error;
-}
-
-static int transfer_to_push_transfer(const git_transfer_progress *stats, void *payload)
-{
- const git_remote_callbacks *cbs = payload;
-
- if (!cbs || !cbs->push_transfer_progress)
- return 0;
-
- return cbs->push_transfer_progress(stats->received_objects, stats->total_objects, stats->received_bytes,
- cbs->payload);
-}
-
-static int local_push(
- git_transport *transport,
- git_push *push,
- const git_remote_callbacks *cbs)
-{
- transport_local *t = (transport_local *)transport;
- git_repository *remote_repo = NULL;
- push_spec *spec;
- char *url = NULL;
- const char *path;
- git_buf buf = GIT_BUF_INIT, odb_path = GIT_BUF_INIT;
- int error;
- size_t j;
-
- GIT_UNUSED(cbs);
-
- /* 'push->remote->url' may be a url or path; convert to a path */
- if ((error = git_path_from_url_or_path(&buf, push->remote->url)) < 0) {
- git_buf_free(&buf);
- return error;
- }
- path = git_buf_cstr(&buf);
-
- error = git_repository_open(&remote_repo, path);
-
- git_buf_free(&buf);
-
- if (error < 0)
- return error;
-
- /* We don't currently support pushing locally to non-bare repos. Proper
- non-bare repo push support would require checking configs to see if
- we should override the default 'don't let this happen' behavior.
-
- Note that this is only an issue when pushing to the current branch,
- but we forbid all pushes just in case */
- if (!remote_repo->is_bare) {
- error = GIT_EBAREREPO;
- giterr_set(GITERR_INVALID, "Local push doesn't (yet) support pushing to non-bare repos.");
- goto on_error;
- }
-
- if ((error = git_buf_joinpath(&odb_path, git_repository_path(remote_repo), "objects/pack")) < 0)
- goto on_error;
-
- error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs);
- git_buf_free(&odb_path);
-
- if (error < 0)
- goto on_error;
-
- push->unpack_ok = 1;
-
- git_vector_foreach(&push->specs, j, spec) {
- push_status *status;
- const git_error *last;
- char *ref = spec->refspec.dst;
-
- status = git__calloc(1, sizeof(push_status));
- if (!status)
- goto on_error;
-
- status->ref = git__strdup(ref);
- if (!status->ref) {
- git_push_status_free(status);
- goto on_error;
- }
-
- error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst,
- &spec->loid, &spec->roid);
-
- switch (error) {
- case GIT_OK:
- break;
- case GIT_EINVALIDSPEC:
- status->msg = git__strdup("funny refname");
- break;
- case GIT_ENOTFOUND:
- status->msg = git__strdup("Remote branch not found to delete");
- break;
- default:
- last = giterr_last();
-
- if (last && last->message)
- status->msg = git__strdup(last->message);
- else
- status->msg = git__strdup("Unspecified error encountered");
- break;
- }
-
- /* failed to allocate memory for a status message */
- if (error < 0 && !status->msg) {
- git_push_status_free(status);
- goto on_error;
- }
-
- /* failed to insert the ref update status */
- if ((error = git_vector_insert(&push->status, status)) < 0) {
- git_push_status_free(status);
- goto on_error;
- }
- }
-
- if (push->specs.length) {
- int flags = t->flags;
- url = git__strdup(t->url);
-
- if (!url || t->parent.close(&t->parent) < 0 ||
- t->parent.connect(&t->parent, url,
- NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags))
- goto on_error;
- }
-
- error = 0;
-
-on_error:
- git_repository_free(remote_repo);
- git__free(url);
-
- return error;
-}
-
-typedef struct foreach_data {
- git_transfer_progress *stats;
- git_transfer_progress_cb progress_cb;
- void *progress_payload;
- git_odb_writepack *writepack;
-} foreach_data;
-
-static int foreach_cb(void *buf, size_t len, void *payload)
-{
- foreach_data *data = (foreach_data*)payload;
-
- data->stats->received_bytes += len;
- return data->writepack->append(data->writepack, buf, len, data->stats);
-}
-
-static const char *counting_objects_fmt = "Counting objects %d\r";
-static const char *compressing_objects_fmt = "Compressing objects: %.0f%% (%d/%d)";
-
-static int local_counting(int stage, unsigned int current, unsigned int total, void *payload)
-{
- git_buf progress_info = GIT_BUF_INIT;
- transport_local *t = payload;
- int error;
-
- if (!t->progress_cb)
- return 0;
-
- if (stage == GIT_PACKBUILDER_ADDING_OBJECTS) {
- git_buf_printf(&progress_info, counting_objects_fmt, current);
- } else if (stage == GIT_PACKBUILDER_DELTAFICATION) {
- float perc = (((float) current) / total) * 100;
- git_buf_printf(&progress_info, compressing_objects_fmt, perc, current, total);
- if (current == total)
- git_buf_printf(&progress_info, ", done\n");
- else
- git_buf_putc(&progress_info, '\r');
-
- }
-
- if (git_buf_oom(&progress_info))
- return -1;
-
- error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload);
- git_buf_free(&progress_info);
-
- return error;
-}
-
-static int local_download_pack(
- git_transport *transport,
- git_repository *repo,
- git_transfer_progress *stats,
- git_transfer_progress_cb progress_cb,
- void *progress_payload)
-{
- transport_local *t = (transport_local*)transport;
- git_revwalk *walk = NULL;
- git_remote_head *rhead;
- unsigned int i;
- int error = -1;
- git_packbuilder *pack = NULL;
- git_odb_writepack *writepack = NULL;
- git_odb *odb = NULL;
- git_buf progress_info = GIT_BUF_INIT;
-
- if ((error = git_revwalk_new(&walk, t->repo)) < 0)
- goto cleanup;
- git_revwalk_sorting(walk, GIT_SORT_TIME);
-
- if ((error = git_packbuilder_new(&pack, t->repo)) < 0)
- goto cleanup;
-
- git_packbuilder_set_callbacks(pack, local_counting, t);
-
- stats->total_objects = 0;
- stats->indexed_objects = 0;
- stats->received_objects = 0;
- stats->received_bytes = 0;
-
- git_vector_foreach(&t->refs, i, rhead) {
- git_object *obj;
- if ((error = git_object_lookup(&obj, t->repo, &rhead->oid, GIT_OBJ_ANY)) < 0)
- goto cleanup;
-
- if (git_object_type(obj) == GIT_OBJ_COMMIT) {
- /* Revwalker includes only wanted commits */
- error = git_revwalk_push(walk, &rhead->oid);
- if (!error && !git_oid_iszero(&rhead->loid)) {
- error = git_revwalk_hide(walk, &rhead->loid);
- if (error == GIT_ENOTFOUND)
- error = 0;
- }
- } else {
- /* Tag or some other wanted object. Add it on its own */
- error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name);
- }
- git_object_free(obj);
- if (error < 0)
- goto cleanup;
- }
-
- if ((error = git_packbuilder_insert_walk(pack, walk)))
- goto cleanup;
-
- if ((error = git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0)
- goto cleanup;
-
- if (t->progress_cb &&
- (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0)
- goto cleanup;
-
- /* Walk the objects, building a packfile */
- if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
- goto cleanup;
-
- /* One last one with the newline */
- git_buf_clear(&progress_info);
- git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack));
- if ((error = git_buf_putc(&progress_info, '\n')) < 0)
- goto cleanup;
-
- if (t->progress_cb &&
- (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0)
- goto cleanup;
-
- if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0)
- goto cleanup;
-
- /* Write the data to the ODB */
- {
- foreach_data data = {0};
- data.stats = stats;
- data.progress_cb = progress_cb;
- data.progress_payload = progress_payload;
- data.writepack = writepack;
-
- /* autodetect */
- git_packbuilder_set_threads(pack, 0);
-
- if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
- goto cleanup;
- }
-
- error = writepack->commit(writepack, stats);
-
-cleanup:
- if (writepack) writepack->free(writepack);
- git_buf_free(&progress_info);
- git_packbuilder_free(pack);
- git_revwalk_free(walk);
- return error;
-}
-
-static int local_set_callbacks(
- git_transport *transport,
- git_transport_message_cb progress_cb,
- git_transport_message_cb error_cb,
- git_transport_certificate_check_cb certificate_check_cb,
- void *message_cb_payload)
-{
- transport_local *t = (transport_local *)transport;
-
- GIT_UNUSED(certificate_check_cb);
-
- t->progress_cb = progress_cb;
- t->error_cb = error_cb;
- t->message_cb_payload = message_cb_payload;
-
- return 0;
-}
-
-static int local_is_connected(git_transport *transport)
-{
- transport_local *t = (transport_local *)transport;
-
- return t->connected;
-}
-
-static int local_read_flags(git_transport *transport, int *flags)
-{
- transport_local *t = (transport_local *)transport;
-
- *flags = t->flags;
-
- return 0;
-}
-
-static void local_cancel(git_transport *transport)
-{
- transport_local *t = (transport_local *)transport;
-
- git_atomic_set(&t->cancelled, 1);
-}
-
-static int local_close(git_transport *transport)
-{
- transport_local *t = (transport_local *)transport;
-
- t->connected = 0;
-
- if (t->repo) {
- git_repository_free(t->repo);
- t->repo = NULL;
- }
-
- if (t->url) {
- git__free(t->url);
- t->url = NULL;
- }
-
- return 0;
-}
-
-static void local_free(git_transport *transport)
-{
- transport_local *t = (transport_local *)transport;
-
- free_heads(&t->refs);
-
- /* Close the transport, if it's still open. */
- local_close(transport);
-
- /* Free the transport */
- git__free(t);
-}
-
-/**************
- * Public API *
- **************/
-
-int git_transport_local(git_transport **out, git_remote *owner, void *param)
-{
- int error;
- transport_local *t;
-
- GIT_UNUSED(param);
-
- t = git__calloc(1, sizeof(transport_local));
- GITERR_CHECK_ALLOC(t);
-
- t->parent.version = GIT_TRANSPORT_VERSION;
- t->parent.set_callbacks = local_set_callbacks;
- t->parent.connect = local_connect;
- t->parent.negotiate_fetch = local_negotiate_fetch;
- t->parent.download_pack = local_download_pack;
- t->parent.push = local_push;
- t->parent.close = local_close;
- t->parent.free = local_free;
- t->parent.ls = local_ls;
- t->parent.is_connected = local_is_connected;
- t->parent.read_flags = local_read_flags;
- t->parent.cancel = local_cancel;
-
- if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) {
- git__free(t);
- return error;
- }
-
- t->owner = owner;
-
- *out = (git_transport *) t;
-
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "git2.h"
-#include "smart.h"
-#include "refs.h"
-#include "refspec.h"
-#include "proxy.h"
-
-static int git_smart__recv_cb(gitno_buffer *buf)
-{
- transport_smart *t = (transport_smart *) buf->cb_data;
- size_t old_len, bytes_read;
- int error;
-
- assert(t->current_stream);
-
- old_len = buf->offset;
-
- if ((error = t->current_stream->read(t->current_stream, buf->data + buf->offset, buf->len - buf->offset, &bytes_read)) < 0)
- return error;
-
- buf->offset += bytes_read;
-
- if (t->packetsize_cb && !t->cancelled.val) {
- error = t->packetsize_cb(bytes_read, t->packetsize_payload);
- if (error) {
- git_atomic_set(&t->cancelled, 1);
- return GIT_EUSER;
- }
- }
-
- return (int)(buf->offset - old_len);
-}
-
-GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransport)
-{
- if (t->current_stream) {
- t->current_stream->free(t->current_stream);
- t->current_stream = NULL;
- }
-
- if (close_subtransport &&
- t->wrapped->close(t->wrapped) < 0)
- return -1;
-
- return 0;
-}
-
-static int git_smart__set_callbacks(
- git_transport *transport,
- git_transport_message_cb progress_cb,
- git_transport_message_cb error_cb,
- git_transport_certificate_check_cb certificate_check_cb,
- void *message_cb_payload)
-{
- transport_smart *t = (transport_smart *)transport;
-
- t->progress_cb = progress_cb;
- t->error_cb = error_cb;
- t->certificate_check_cb = certificate_check_cb;
- t->message_cb_payload = message_cb_payload;
-
- return 0;
-}
-
-static int http_header_name_length(const char *http_header)
-{
- const char *colon = strchr(http_header, ':');
- if (!colon)
- return 0;
- return colon - http_header;
-}
-
-static bool is_malformed_http_header(const char *http_header)
-{
- const char *c;
- int name_len;
-
- // Disallow \r and \n
- c = strchr(http_header, '\r');
- if (c)
- return true;
- c = strchr(http_header, '\n');
- if (c)
- return true;
-
- // Require a header name followed by :
- name_len = http_header_name_length(http_header);
- if (name_len < 1)
- return true;
-
- return false;
-}
-
-static char *forbidden_custom_headers[] = {
- "User-Agent",
- "Host",
- "Accept",
- "Content-Type",
- "Transfer-Encoding",
- "Content-Length",
-};
-
-static bool is_forbidden_custom_header(const char *custom_header)
-{
- unsigned long i;
- int name_len = http_header_name_length(custom_header);
-
- // Disallow headers that we set
- for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++)
- if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0)
- return true;
-
- return false;
-}
-
-static int git_smart__set_custom_headers(
- git_transport *transport,
- const git_strarray *custom_headers)
-{
- transport_smart *t = (transport_smart *)transport;
- size_t i;
-
- if (t->custom_headers.count)
- git_strarray_free(&t->custom_headers);
-
- if (!custom_headers)
- return 0;
-
- for (i = 0; i < custom_headers->count; i++) {
- if (is_malformed_http_header(custom_headers->strings[i])) {
- giterr_set(GITERR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]);
- return -1;
- }
- if (is_forbidden_custom_header(custom_headers->strings[i])) {
- giterr_set(GITERR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]);
- return -1;
- }
- }
-
- return git_strarray_copy(&t->custom_headers, custom_headers);
-}
-
-int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
-{
- size_t i;
- git_pkt *pkt;
-
- git_vector_clear(&t->heads);
- git_vector_foreach(&t->refs, i, pkt) {
- git_pkt_ref *ref = (git_pkt_ref *) pkt;
- if (pkt->type != GIT_PKT_REF)
- continue;
-
- if (symrefs) {
- git_refspec *spec;
- git_buf buf = GIT_BUF_INIT;
- size_t j;
- int error = 0;
-
- git_vector_foreach(symrefs, j, spec) {
- git_buf_clear(&buf);
- if (git_refspec_src_matches(spec, ref->head.name) &&
- !(error = git_refspec_transform(&buf, spec, ref->head.name)))
- ref->head.symref_target = git_buf_detach(&buf);
- }
-
- git_buf_free(&buf);
-
- if (error < 0)
- return error;
- }
-
- if (git_vector_insert(&t->heads, &ref->head) < 0)
- return -1;
- }
-
- return 0;
-}
-
-static void free_symrefs(git_vector *symrefs)
-{
- git_refspec *spec;
- size_t i;
-
- git_vector_foreach(symrefs, i, spec) {
- git_refspec__free(spec);
- git__free(spec);
- }
-
- git_vector_free(symrefs);
-}
-
-static int git_smart__connect(
- git_transport *transport,
- const char *url,
- git_cred_acquire_cb cred_acquire_cb,
- void *cred_acquire_payload,
- const git_proxy_options *proxy,
- int direction,
- int flags)
-{
- transport_smart *t = (transport_smart *)transport;
- git_smart_subtransport_stream *stream;
- int error;
- git_pkt *pkt;
- git_pkt_ref *first;
- git_vector symrefs;
- git_smart_service_t service;
-
- if (git_smart__reset_stream(t, true) < 0)
- return -1;
-
- t->url = git__strdup(url);
- GITERR_CHECK_ALLOC(t->url);
-
- if (git_proxy_options_dup(&t->proxy, proxy) < 0)
- return -1;
-
- t->direction = direction;
- t->flags = flags;
- t->cred_acquire_cb = cred_acquire_cb;
- t->cred_acquire_payload = cred_acquire_payload;
-
- if (GIT_DIRECTION_FETCH == t->direction)
- service = GIT_SERVICE_UPLOADPACK_LS;
- else if (GIT_DIRECTION_PUSH == t->direction)
- service = GIT_SERVICE_RECEIVEPACK_LS;
- else {
- giterr_set(GITERR_NET, "Invalid direction");
- return -1;
- }
-
- if ((error = t->wrapped->action(&stream, t->wrapped, t->url, service)) < 0)
- return error;
-
- /* Save off the current stream (i.e. socket) that we are working with */
- t->current_stream = stream;
-
- gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
-
- /* 2 flushes for RPC; 1 for stateful */
- if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0)
- return error;
-
- /* Strip the comment packet for RPC */
- if (t->rpc) {
- pkt = (git_pkt *)git_vector_get(&t->refs, 0);
-
- if (!pkt || GIT_PKT_COMMENT != pkt->type) {
- giterr_set(GITERR_NET, "Invalid response");
- return -1;
- } else {
- /* Remove the comment pkt from the list */
- git_vector_remove(&t->refs, 0);
- git__free(pkt);
- }
- }
-
- /* We now have loaded the refs. */
- t->have_refs = 1;
-
- first = (git_pkt_ref *)git_vector_get(&t->refs, 0);
-
- if ((error = git_vector_init(&symrefs, 1, NULL)) < 0)
- return error;
-
- /* Detect capabilities */
- if (git_smart__detect_caps(first, &t->caps, &symrefs) < 0)
- return -1;
-
- /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
- if (1 == t->refs.length && !strcmp(first->head.name, "capabilities^{}") &&
- git_oid_iszero(&first->head.oid)) {
- git_vector_clear(&t->refs);
- git_pkt_free((git_pkt *)first);
- }
-
- /* Keep a list of heads for _ls */
- git_smart__update_heads(t, &symrefs);
-
- free_symrefs(&symrefs);
-
- if (t->rpc && git_smart__reset_stream(t, false) < 0)
- return -1;
-
- /* We're now logically connected. */
- t->connected = 1;
-
- return 0;
-}
-
-static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
-{
- transport_smart *t = (transport_smart *)transport;
-
- if (!t->have_refs) {
- giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
- return -1;
- }
-
- *out = (const git_remote_head **) t->heads.contents;
- *size = t->heads.length;
-
- return 0;
-}
-
-int git_smart__negotiation_step(git_transport *transport, void *data, size_t len)
-{
- transport_smart *t = (transport_smart *)transport;
- git_smart_subtransport_stream *stream;
- int error;
-
- if (t->rpc && git_smart__reset_stream(t, false) < 0)
- return -1;
-
- if (GIT_DIRECTION_FETCH != t->direction) {
- giterr_set(GITERR_NET, "This operation is only valid for fetch");
- return -1;
- }
-
- if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) < 0)
- return error;
-
- /* If this is a stateful implementation, the stream we get back should be the same */
- assert(t->rpc || t->current_stream == stream);
-
- /* Save off the current stream (i.e. socket) that we are working with */
- t->current_stream = stream;
-
- if ((error = stream->write(stream, (const char *)data, len)) < 0)
- return error;
-
- gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
-
- return 0;
-}
-
-int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **stream)
-{
- int error;
-
- if (t->rpc && git_smart__reset_stream(t, false) < 0)
- return -1;
-
- if (GIT_DIRECTION_PUSH != t->direction) {
- giterr_set(GITERR_NET, "This operation is only valid for push");
- return -1;
- }
-
- if ((error = t->wrapped->action(stream, t->wrapped, t->url, GIT_SERVICE_RECEIVEPACK)) < 0)
- return error;
-
- /* If this is a stateful implementation, the stream we get back should be the same */
- assert(t->rpc || t->current_stream == *stream);
-
- /* Save off the current stream (i.e. socket) that we are working with */
- t->current_stream = *stream;
-
- gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
-
- return 0;
-}
-
-static void git_smart__cancel(git_transport *transport)
-{
- transport_smart *t = (transport_smart *)transport;
-
- git_atomic_set(&t->cancelled, 1);
-}
-
-static int git_smart__is_connected(git_transport *transport)
-{
- transport_smart *t = (transport_smart *)transport;
-
- return t->connected;
-}
-
-static int git_smart__read_flags(git_transport *transport, int *flags)
-{
- transport_smart *t = (transport_smart *)transport;
-
- *flags = t->flags;
-
- return 0;
-}
-
-static int git_smart__close(git_transport *transport)
-{
- transport_smart *t = (transport_smart *)transport;
- git_vector *common = &t->common;
- unsigned int i;
- git_pkt *p;
- int ret;
- git_smart_subtransport_stream *stream;
- const char flush[] = "0000";
-
- /*
- * If we're still connected at this point and not using RPC,
- * we should say goodbye by sending a flush, or git-daemon
- * will complain that we disconnected unexpectedly.
- */
- if (t->connected && !t->rpc &&
- !t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) {
- t->current_stream->write(t->current_stream, flush, 4);
- }
-
- ret = git_smart__reset_stream(t, true);
-
- git_vector_foreach(common, i, p)
- git_pkt_free(p);
-
- git_vector_free(common);
-
- if (t->url) {
- git__free(t->url);
- t->url = NULL;
- }
-
- t->connected = 0;
-
- return ret;
-}
-
-static void git_smart__free(git_transport *transport)
-{
- transport_smart *t = (transport_smart *)transport;
- git_vector *refs = &t->refs;
- unsigned int i;
- git_pkt *p;
-
- /* Make sure that the current stream is closed, if we have one. */
- git_smart__close(transport);
-
- /* Free the subtransport */
- t->wrapped->free(t->wrapped);
-
- git_vector_free(&t->heads);
- git_vector_foreach(refs, i, p)
- git_pkt_free(p);
-
- git_vector_free(refs);
- git__free((char *)t->proxy.url);
-
- git_strarray_free(&t->custom_headers);
-
- git__free(t);
-}
-
-static int ref_name_cmp(const void *a, const void *b)
-{
- const git_pkt_ref *ref_a = a, *ref_b = b;
-
- return strcmp(ref_a->head.name, ref_b->head.name);
-}
-
-int git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname)
-{
- transport_smart *t = (transport_smart *)transport;
-
- return t->certificate_check_cb(cert, valid, hostname, t->message_cb_payload);
-}
-
-int git_transport_smart_credentials(git_cred **out, git_transport *transport, const char *user, int methods)
-{
- transport_smart *t = (transport_smart *)transport;
-
- return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload);
-}
-
-int git_transport_smart(git_transport **out, git_remote *owner, void *param)
-{
- transport_smart *t;
- git_smart_subtransport_definition *definition = (git_smart_subtransport_definition *)param;
-
- if (!param)
- return -1;
-
- t = git__calloc(1, sizeof(transport_smart));
- GITERR_CHECK_ALLOC(t);
-
- t->parent.version = GIT_TRANSPORT_VERSION;
- t->parent.set_callbacks = git_smart__set_callbacks;
- t->parent.set_custom_headers = git_smart__set_custom_headers;
- t->parent.connect = git_smart__connect;
- t->parent.close = git_smart__close;
- t->parent.free = git_smart__free;
- t->parent.negotiate_fetch = git_smart__negotiate_fetch;
- t->parent.download_pack = git_smart__download_pack;
- t->parent.push = git_smart__push;
- t->parent.ls = git_smart__ls;
- t->parent.is_connected = git_smart__is_connected;
- t->parent.read_flags = git_smart__read_flags;
- t->parent.cancel = git_smart__cancel;
-
- t->owner = owner;
- t->rpc = definition->rpc;
-
- if (git_vector_init(&t->refs, 16, ref_name_cmp) < 0) {
- git__free(t);
- return -1;
- }
-
- if (git_vector_init(&t->heads, 16, ref_name_cmp) < 0) {
- git__free(t);
- return -1;
- }
-
- if (definition->callback(&t->wrapped, &t->parent, definition->param) < 0) {
- git__free(t);
- return -1;
- }
-
- *out = (git_transport *) t;
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "git2.h"
-#include "vector.h"
-#include "netops.h"
-#include "buffer.h"
-#include "push.h"
-#include "git2/sys/transport.h"
-
-#define GIT_SIDE_BAND_DATA 1
-#define GIT_SIDE_BAND_PROGRESS 2
-#define GIT_SIDE_BAND_ERROR 3
-
-#define GIT_CAP_OFS_DELTA "ofs-delta"
-#define GIT_CAP_MULTI_ACK "multi_ack"
-#define GIT_CAP_MULTI_ACK_DETAILED "multi_ack_detailed"
-#define GIT_CAP_SIDE_BAND "side-band"
-#define GIT_CAP_SIDE_BAND_64K "side-band-64k"
-#define GIT_CAP_INCLUDE_TAG "include-tag"
-#define GIT_CAP_DELETE_REFS "delete-refs"
-#define GIT_CAP_REPORT_STATUS "report-status"
-#define GIT_CAP_THIN_PACK "thin-pack"
-#define GIT_CAP_SYMREF "symref"
-
-enum git_pkt_type {
- GIT_PKT_CMD,
- GIT_PKT_FLUSH,
- GIT_PKT_REF,
- GIT_PKT_HAVE,
- GIT_PKT_ACK,
- GIT_PKT_NAK,
- GIT_PKT_PACK,
- GIT_PKT_COMMENT,
- GIT_PKT_ERR,
- GIT_PKT_DATA,
- GIT_PKT_PROGRESS,
- GIT_PKT_OK,
- GIT_PKT_NG,
- GIT_PKT_UNPACK,
-};
-
-/* Used for multi_ack and mutli_ack_detailed */
-enum git_ack_status {
- GIT_ACK_NONE,
- GIT_ACK_CONTINUE,
- GIT_ACK_COMMON,
- GIT_ACK_READY
-};
-
-/* This would be a flush pkt */
-typedef struct {
- enum git_pkt_type type;
-} git_pkt;
-
-struct git_pkt_cmd {
- enum git_pkt_type type;
- char *cmd;
- char *path;
- char *host;
-};
-
-/* This is a pkt-line with some info in it */
-typedef struct {
- enum git_pkt_type type;
- git_remote_head head;
- char *capabilities;
-} git_pkt_ref;
-
-/* Useful later */
-typedef struct {
- enum git_pkt_type type;
- git_oid oid;
- enum git_ack_status status;
-} git_pkt_ack;
-
-typedef struct {
- enum git_pkt_type type;
- char comment[GIT_FLEX_ARRAY];
-} git_pkt_comment;
-
-typedef struct {
- enum git_pkt_type type;
- int len;
- char data[GIT_FLEX_ARRAY];
-} git_pkt_data;
-
-typedef git_pkt_data git_pkt_progress;
-
-typedef struct {
- enum git_pkt_type type;
- int len;
- char error[GIT_FLEX_ARRAY];
-} git_pkt_err;
-
-typedef struct {
- enum git_pkt_type type;
- char *ref;
-} git_pkt_ok;
-
-typedef struct {
- enum git_pkt_type type;
- char *ref;
- char *msg;
-} git_pkt_ng;
-
-typedef struct {
- enum git_pkt_type type;
- int unpack_ok;
-} git_pkt_unpack;
-
-typedef struct transport_smart_caps {
- int common:1,
- ofs_delta:1,
- multi_ack: 1,
- multi_ack_detailed: 1,
- side_band:1,
- side_band_64k:1,
- include_tag:1,
- delete_refs:1,
- report_status:1,
- thin_pack:1;
-} transport_smart_caps;
-
-typedef int (*packetsize_cb)(size_t received, void *payload);
-
-typedef struct {
- git_transport parent;
- git_remote *owner;
- char *url;
- git_cred_acquire_cb cred_acquire_cb;
- void *cred_acquire_payload;
- git_proxy_options proxy;
- int direction;
- int flags;
- git_transport_message_cb progress_cb;
- git_transport_message_cb error_cb;
- git_transport_certificate_check_cb certificate_check_cb;
- void *message_cb_payload;
- git_strarray custom_headers;
- git_smart_subtransport *wrapped;
- git_smart_subtransport_stream *current_stream;
- transport_smart_caps caps;
- git_vector refs;
- git_vector heads;
- git_vector common;
- git_atomic cancelled;
- packetsize_cb packetsize_cb;
- void *packetsize_payload;
- unsigned rpc : 1,
- have_refs : 1,
- connected : 1;
- gitno_buffer buffer;
- char buffer_data[65536];
-} transport_smart;
-
-/* smart_protocol.c */
-int git_smart__store_refs(transport_smart *t, int flushes);
-int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs);
-int git_smart__push(git_transport *transport, git_push *push, const git_remote_callbacks *cbs);
-
-int git_smart__negotiate_fetch(
- git_transport *transport,
- git_repository *repo,
- const git_remote_head * const *refs,
- size_t count);
-
-int git_smart__download_pack(
- git_transport *transport,
- git_repository *repo,
- git_transfer_progress *stats,
- git_transfer_progress_cb progress_cb,
- void *progress_payload);
-
-/* smart.c */
-int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
-int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out);
-
-int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
-
-/* smart_pkt.c */
-int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
-int git_pkt_buffer_flush(git_buf *buf);
-int git_pkt_send_flush(GIT_SOCKET s);
-int git_pkt_buffer_done(git_buf *buf);
-int git_pkt_buffer_wants(const git_remote_head * const *refs, size_t count, transport_smart_caps *caps, git_buf *buf);
-int git_pkt_buffer_have(git_oid *oid, git_buf *buf);
-void git_pkt_free(git_pkt *pkt);
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-
-#include "git2/types.h"
-#include "git2/errors.h"
-#include "git2/refs.h"
-#include "git2/revwalk.h"
-
-#include "smart.h"
-#include "util.h"
-#include "netops.h"
-#include "posix.h"
-#include "buffer.h"
-
-#include <ctype.h>
-
-#define PKT_LEN_SIZE 4
-static const char pkt_done_str[] = "0009done\n";
-static const char pkt_flush_str[] = "0000";
-static const char pkt_have_prefix[] = "0032have ";
-static const char pkt_want_prefix[] = "0032want ";
-
-static int flush_pkt(git_pkt **out)
-{
- git_pkt *pkt;
-
- pkt = git__malloc(sizeof(git_pkt));
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_FLUSH;
- *out = pkt;
-
- return 0;
-}
-
-/* the rest of the line will be useful for multi_ack and multi_ack_detailed */
-static int ack_pkt(git_pkt **out, const char *line, size_t len)
-{
- git_pkt_ack *pkt;
- GIT_UNUSED(line);
- GIT_UNUSED(len);
-
- pkt = git__calloc(1, sizeof(git_pkt_ack));
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_ACK;
- line += 3;
- len -= 3;
-
- if (len >= GIT_OID_HEXSZ) {
- git_oid_fromstr(&pkt->oid, line + 1);
- line += GIT_OID_HEXSZ + 1;
- len -= GIT_OID_HEXSZ + 1;
- }
-
- if (len >= 7) {
- if (!git__prefixcmp(line + 1, "continue"))
- pkt->status = GIT_ACK_CONTINUE;
- if (!git__prefixcmp(line + 1, "common"))
- pkt->status = GIT_ACK_COMMON;
- if (!git__prefixcmp(line + 1, "ready"))
- pkt->status = GIT_ACK_READY;
- }
-
- *out = (git_pkt *) pkt;
-
- return 0;
-}
-
-static int nak_pkt(git_pkt **out)
-{
- git_pkt *pkt;
-
- pkt = git__malloc(sizeof(git_pkt));
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_NAK;
- *out = pkt;
-
- return 0;
-}
-
-static int pack_pkt(git_pkt **out)
-{
- git_pkt *pkt;
-
- pkt = git__malloc(sizeof(git_pkt));
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_PACK;
- *out = pkt;
-
- return 0;
-}
-
-static int comment_pkt(git_pkt **out, const char *line, size_t len)
-{
- git_pkt_comment *pkt;
- size_t alloclen;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_comment), len);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
- pkt = git__malloc(alloclen);
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_COMMENT;
- memcpy(pkt->comment, line, len);
- pkt->comment[len] = '\0';
-
- *out = (git_pkt *) pkt;
-
- return 0;
-}
-
-static int err_pkt(git_pkt **out, const char *line, size_t len)
-{
- git_pkt_err *pkt;
- size_t alloclen;
-
- /* Remove "ERR " from the line */
- line += 4;
- len -= 4;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
- GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
- pkt = git__malloc(alloclen);
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_ERR;
- pkt->len = (int)len;
- memcpy(pkt->error, line, len);
- pkt->error[len] = '\0';
-
- *out = (git_pkt *) pkt;
-
- return 0;
-}
-
-static int data_pkt(git_pkt **out, const char *line, size_t len)
-{
- git_pkt_data *pkt;
- size_t alloclen;
-
- line++;
- len--;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
- pkt = git__malloc(alloclen);
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_DATA;
- pkt->len = (int) len;
- memcpy(pkt->data, line, len);
-
- *out = (git_pkt *) pkt;
-
- return 0;
-}
-
-static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len)
-{
- git_pkt_progress *pkt;
- size_t alloclen;
-
- line++;
- len--;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
- pkt = git__malloc(alloclen);
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_PROGRESS;
- pkt->len = (int) len;
- memcpy(pkt->data, line, len);
-
- *out = (git_pkt *) pkt;
-
- return 0;
-}
-
-static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
-{
- git_pkt_err *pkt;
- size_t alloc_len;
-
- line++;
- len--;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(git_pkt_err), len);
- GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
- pkt = git__malloc(alloc_len);
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_ERR;
- pkt->len = (int)len;
- memcpy(pkt->error, line, len);
- pkt->error[len] = '\0';
-
- *out = (git_pkt *)pkt;
-
- return 0;
-}
-
-/*
- * Parse an other-ref line.
- */
-static int ref_pkt(git_pkt **out, const char *line, size_t len)
-{
- int error;
- git_pkt_ref *pkt;
- size_t alloclen;
-
- pkt = git__malloc(sizeof(git_pkt_ref));
- GITERR_CHECK_ALLOC(pkt);
-
- memset(pkt, 0x0, sizeof(git_pkt_ref));
- pkt->type = GIT_PKT_REF;
- if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0)
- goto error_out;
-
- /* Check for a bit of consistency */
- if (line[GIT_OID_HEXSZ] != ' ') {
- giterr_set(GITERR_NET, "Error parsing pkt-line");
- error = -1;
- goto error_out;
- }
-
- /* Jump from the name */
- line += GIT_OID_HEXSZ + 1;
- len -= (GIT_OID_HEXSZ + 1);
-
- if (line[len - 1] == '\n')
- --len;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
- pkt->head.name = git__malloc(alloclen);
- GITERR_CHECK_ALLOC(pkt->head.name);
-
- memcpy(pkt->head.name, line, len);
- pkt->head.name[len] = '\0';
-
- if (strlen(pkt->head.name) < len) {
- pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
- }
-
- *out = (git_pkt *)pkt;
- return 0;
-
-error_out:
- git__free(pkt);
- return error;
-}
-
-static int ok_pkt(git_pkt **out, const char *line, size_t len)
-{
- git_pkt_ok *pkt;
- const char *ptr;
- size_t alloc_len;
-
- pkt = git__malloc(sizeof(*pkt));
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_OK;
-
- line += 3; /* skip "ok " */
- if (!(ptr = strchr(line, '\n'))) {
- giterr_set(GITERR_NET, "Invalid packet line");
- git__free(pkt);
- return -1;
- }
- len = ptr - line;
-
- GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
- pkt->ref = git__malloc(alloc_len);
- GITERR_CHECK_ALLOC(pkt->ref);
-
- memcpy(pkt->ref, line, len);
- pkt->ref[len] = '\0';
-
- *out = (git_pkt *)pkt;
- return 0;
-}
-
-static int ng_pkt(git_pkt **out, const char *line, size_t len)
-{
- git_pkt_ng *pkt;
- const char *ptr;
- size_t alloclen;
-
- pkt = git__malloc(sizeof(*pkt));
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->ref = NULL;
- pkt->type = GIT_PKT_NG;
-
- line += 3; /* skip "ng " */
- if (!(ptr = strchr(line, ' ')))
- goto out_err;
- len = ptr - line;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
- pkt->ref = git__malloc(alloclen);
- GITERR_CHECK_ALLOC(pkt->ref);
-
- memcpy(pkt->ref, line, len);
- pkt->ref[len] = '\0';
-
- line = ptr + 1;
- if (!(ptr = strchr(line, '\n')))
- goto out_err;
- len = ptr - line;
-
- GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
- pkt->msg = git__malloc(alloclen);
- GITERR_CHECK_ALLOC(pkt->msg);
-
- memcpy(pkt->msg, line, len);
- pkt->msg[len] = '\0';
-
- *out = (git_pkt *)pkt;
- return 0;
-
-out_err:
- giterr_set(GITERR_NET, "Invalid packet line");
- git__free(pkt->ref);
- git__free(pkt);
- return -1;
-}
-
-static int unpack_pkt(git_pkt **out, const char *line, size_t len)
-{
- git_pkt_unpack *pkt;
-
- GIT_UNUSED(len);
-
- pkt = git__malloc(sizeof(*pkt));
- GITERR_CHECK_ALLOC(pkt);
-
- pkt->type = GIT_PKT_UNPACK;
- if (!git__prefixcmp(line, "unpack ok"))
- pkt->unpack_ok = 1;
- else
- pkt->unpack_ok = 0;
-
- *out = (git_pkt *)pkt;
- return 0;
-}
-
-static int32_t parse_len(const char *line)
-{
- char num[PKT_LEN_SIZE + 1];
- int i, k, error;
- int32_t len;
- const char *num_end;
-
- memcpy(num, line, PKT_LEN_SIZE);
- num[PKT_LEN_SIZE] = '\0';
-
- for (i = 0; i < PKT_LEN_SIZE; ++i) {
- if (!isxdigit(num[i])) {
- /* Make sure there are no special characters before passing to error message */
- for (k = 0; k < PKT_LEN_SIZE; ++k) {
- if(!isprint(num[k])) {
- num[k] = '.';
- }
- }
-
- giterr_set(GITERR_NET, "invalid hex digit in length: '%s'", num);
- return -1;
- }
- }
-
- if ((error = git__strtol32(&len, num, &num_end, 16)) < 0)
- return error;
-
- return len;
-}
-
-/*
- * As per the documentation, the syntax is:
- *
- * pkt-line = data-pkt / flush-pkt
- * data-pkt = pkt-len pkt-payload
- * pkt-len = 4*(HEXDIG)
- * pkt-payload = (pkt-len -4)*(OCTET)
- * flush-pkt = "0000"
- *
- * Which means that the first four bytes are the length of the line,
- * in ASCII hexadecimal (including itself)
- */
-
-int git_pkt_parse_line(
- git_pkt **head, const char *line, const char **out, size_t bufflen)
-{
- int ret;
- int32_t len;
-
- /* Not even enough for the length */
- if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
- return GIT_EBUFS;
-
- len = parse_len(line);
- if (len < 0) {
- /*
- * If we fail to parse the length, it might be because the
- * server is trying to send us the packfile already.
- */
- if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) {
- giterr_clear();
- *out = line;
- return pack_pkt(head);
- }
-
- return (int)len;
- }
-
- /*
- * If we were given a buffer length, then make sure there is
- * enough in the buffer to satisfy this line
- */
- if (bufflen > 0 && bufflen < (size_t)len)
- return GIT_EBUFS;
-
- line += PKT_LEN_SIZE;
- /*
- * TODO: How do we deal with empty lines? Try again? with the next
- * line?
- */
- if (len == PKT_LEN_SIZE) {
- *head = NULL;
- *out = line;
- return 0;
- }
-
- if (len == 0) { /* Flush pkt */
- *out = line;
- return flush_pkt(head);
- }
-
- len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
-
- if (*line == GIT_SIDE_BAND_DATA)
- ret = data_pkt(head, line, len);
- else if (*line == GIT_SIDE_BAND_PROGRESS)
- ret = sideband_progress_pkt(head, line, len);
- else if (*line == GIT_SIDE_BAND_ERROR)
- ret = sideband_error_pkt(head, line, len);
- else if (!git__prefixcmp(line, "ACK"))
- ret = ack_pkt(head, line, len);
- else if (!git__prefixcmp(line, "NAK"))
- ret = nak_pkt(head);
- else if (!git__prefixcmp(line, "ERR "))
- ret = err_pkt(head, line, len);
- else if (*line == '#')
- ret = comment_pkt(head, line, len);
- else if (!git__prefixcmp(line, "ok"))
- ret = ok_pkt(head, line, len);
- else if (!git__prefixcmp(line, "ng"))
- ret = ng_pkt(head, line, len);
- else if (!git__prefixcmp(line, "unpack"))
- ret = unpack_pkt(head, line, len);
- else
- ret = ref_pkt(head, line, len);
-
- *out = line + len;
-
- return ret;
-}
-
-void git_pkt_free(git_pkt *pkt)
-{
- if (pkt->type == GIT_PKT_REF) {
- git_pkt_ref *p = (git_pkt_ref *) pkt;
- git__free(p->head.name);
- git__free(p->head.symref_target);
- }
-
- if (pkt->type == GIT_PKT_OK) {
- git_pkt_ok *p = (git_pkt_ok *) pkt;
- git__free(p->ref);
- }
-
- if (pkt->type == GIT_PKT_NG) {
- git_pkt_ng *p = (git_pkt_ng *) pkt;
- git__free(p->ref);
- git__free(p->msg);
- }
-
- git__free(pkt);
-}
-
-int git_pkt_buffer_flush(git_buf *buf)
-{
- return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
-}
-
-static int buffer_want_with_caps(const git_remote_head *head, transport_smart_caps *caps, git_buf *buf)
-{
- git_buf str = GIT_BUF_INIT;
- char oid[GIT_OID_HEXSZ +1] = {0};
- size_t len;
-
- /* Prefer multi_ack_detailed */
- if (caps->multi_ack_detailed)
- git_buf_puts(&str, GIT_CAP_MULTI_ACK_DETAILED " ");
- else if (caps->multi_ack)
- git_buf_puts(&str, GIT_CAP_MULTI_ACK " ");
-
- /* Prefer side-band-64k if the server supports both */
- if (caps->side_band_64k)
- git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K);
- else if (caps->side_band)
- git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND);
-
- if (caps->include_tag)
- git_buf_puts(&str, GIT_CAP_INCLUDE_TAG " ");
-
- if (caps->thin_pack)
- git_buf_puts(&str, GIT_CAP_THIN_PACK " ");
-
- if (caps->ofs_delta)
- git_buf_puts(&str, GIT_CAP_OFS_DELTA " ");
-
- if (git_buf_oom(&str))
- return -1;
-
- len = strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ +
- git_buf_len(&str) + 1 /* LF */;
-
- if (len > 0xffff) {
- giterr_set(GITERR_NET,
- "Tried to produce packet with invalid length %" PRIuZ, len);
- return -1;
- }
-
- git_buf_grow_by(buf, len);
- git_oid_fmt(oid, &head->oid);
- git_buf_printf(buf,
- "%04xwant %s %s\n", (unsigned int)len, oid, git_buf_cstr(&str));
- git_buf_free(&str);
-
- GITERR_CHECK_ALLOC_BUF(buf);
-
- return 0;
-}
-
-/*
- * All "want" packets have the same length and format, so what we do
- * is overwrite the OID each time.
- */
-
-int git_pkt_buffer_wants(
- const git_remote_head * const *refs,
- size_t count,
- transport_smart_caps *caps,
- git_buf *buf)
-{
- size_t i = 0;
- const git_remote_head *head;
-
- if (caps->common) {
- for (; i < count; ++i) {
- head = refs[i];
- if (!head->local)
- break;
- }
-
- if (buffer_want_with_caps(refs[i], caps, buf) < 0)
- return -1;
-
- i++;
- }
-
- for (; i < count; ++i) {
- char oid[GIT_OID_HEXSZ];
-
- head = refs[i];
- if (head->local)
- continue;
-
- git_oid_fmt(oid, &head->oid);
- git_buf_put(buf, pkt_want_prefix, strlen(pkt_want_prefix));
- git_buf_put(buf, oid, GIT_OID_HEXSZ);
- git_buf_putc(buf, '\n');
- if (git_buf_oom(buf))
- return -1;
- }
-
- return git_pkt_buffer_flush(buf);
-}
-
-int git_pkt_buffer_have(git_oid *oid, git_buf *buf)
-{
- char oidhex[GIT_OID_HEXSZ + 1];
-
- memset(oidhex, 0x0, sizeof(oidhex));
- git_oid_fmt(oidhex, oid);
- return git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
-}
-
-int git_pkt_buffer_done(git_buf *buf)
-{
- return git_buf_puts(buf, pkt_done_str);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "git2.h"
-#include "git2/odb_backend.h"
-
-#include "smart.h"
-#include "refs.h"
-#include "repository.h"
-#include "push.h"
-#include "pack-objects.h"
-#include "remote.h"
-#include "util.h"
-
-#define NETWORK_XFER_THRESHOLD (100*1024)
-/* The minimal interval between progress updates (in seconds). */
-#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
-
-int git_smart__store_refs(transport_smart *t, int flushes)
-{
- gitno_buffer *buf = &t->buffer;
- git_vector *refs = &t->refs;
- int error, flush = 0, recvd;
- const char *line_end = NULL;
- git_pkt *pkt = NULL;
- size_t i;
-
- /* Clear existing refs in case git_remote_connect() is called again
- * after git_remote_disconnect().
- */
- git_vector_foreach(refs, i, pkt) {
- git_pkt_free(pkt);
- }
- git_vector_clear(refs);
- pkt = NULL;
-
- do {
- if (buf->offset > 0)
- error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset);
- else
- error = GIT_EBUFS;
-
- if (error < 0 && error != GIT_EBUFS)
- return error;
-
- if (error == GIT_EBUFS) {
- if ((recvd = gitno_recv(buf)) < 0)
- return recvd;
-
- if (recvd == 0) {
- giterr_set(GITERR_NET, "early EOF");
- return GIT_EEOF;
- }
-
- continue;
- }
-
- gitno_consume(buf, line_end);
- if (pkt->type == GIT_PKT_ERR) {
- giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
- git__free(pkt);
- return -1;
- }
-
- if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0)
- return -1;
-
- if (pkt->type == GIT_PKT_FLUSH) {
- flush++;
- git_pkt_free(pkt);
- }
- } while (flush < flushes);
-
- return flush;
-}
-
-static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
-{
- int error;
- const char *end;
- git_buf buf = GIT_BUF_INIT;
- git_refspec *mapping = NULL;
-
- ptr += strlen(GIT_CAP_SYMREF);
- if (*ptr != '=')
- goto on_invalid;
-
- ptr++;
- if (!(end = strchr(ptr, ' ')) &&
- !(end = strchr(ptr, '\0')))
- goto on_invalid;
-
- if ((error = git_buf_put(&buf, ptr, end - ptr)) < 0)
- return error;
-
- /* symref mapping has refspec format */
- mapping = git__calloc(1, sizeof(git_refspec));
- GITERR_CHECK_ALLOC(mapping);
-
- error = git_refspec__parse(mapping, git_buf_cstr(&buf), true);
- git_buf_free(&buf);
-
- /* if the error isn't OOM, then it's a parse error; let's use a nicer message */
- if (error < 0) {
- if (giterr_last()->klass != GITERR_NOMEMORY)
- goto on_invalid;
-
- git__free(mapping);
- return error;
- }
-
- if ((error = git_vector_insert(symrefs, mapping)) < 0)
- return error;
-
- *out = end;
- return 0;
-
-on_invalid:
- giterr_set(GITERR_NET, "remote sent invalid symref");
- git_refspec__free(mapping);
- git__free(mapping);
- return -1;
-}
-
-int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
-{
- const char *ptr;
-
- /* No refs or capabilites, odd but not a problem */
- if (pkt == NULL || pkt->capabilities == NULL)
- return 0;
-
- ptr = pkt->capabilities;
- while (ptr != NULL && *ptr != '\0') {
- if (*ptr == ' ')
- ptr++;
-
- if (!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
- caps->common = caps->ofs_delta = 1;
- ptr += strlen(GIT_CAP_OFS_DELTA);
- continue;
- }
-
- /* Keep multi_ack_detailed before multi_ack */
- if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK_DETAILED)) {
- caps->common = caps->multi_ack_detailed = 1;
- ptr += strlen(GIT_CAP_MULTI_ACK_DETAILED);
- continue;
- }
-
- if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
- caps->common = caps->multi_ack = 1;
- ptr += strlen(GIT_CAP_MULTI_ACK);
- continue;
- }
-
- if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
- caps->common = caps->include_tag = 1;
- ptr += strlen(GIT_CAP_INCLUDE_TAG);
- continue;
- }
-
- /* Keep side-band check after side-band-64k */
- if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
- caps->common = caps->side_band_64k = 1;
- ptr += strlen(GIT_CAP_SIDE_BAND_64K);
- continue;
- }
-
- if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
- caps->common = caps->side_band = 1;
- ptr += strlen(GIT_CAP_SIDE_BAND);
- continue;
- }
-
- if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) {
- caps->common = caps->delete_refs = 1;
- ptr += strlen(GIT_CAP_DELETE_REFS);
- continue;
- }
-
- if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
- caps->common = caps->thin_pack = 1;
- ptr += strlen(GIT_CAP_THIN_PACK);
- continue;
- }
-
- if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) {
- int error;
-
- if ((error = append_symref(&ptr, symrefs, ptr)) < 0)
- return error;
-
- continue;
- }
-
- /* We don't know this capability, so skip it */
- ptr = strchr(ptr, ' ');
- }
-
- return 0;
-}
-
-static int recv_pkt(git_pkt **out, gitno_buffer *buf)
-{
- const char *ptr = buf->data, *line_end = ptr;
- git_pkt *pkt = NULL;
- int pkt_type, error = 0, ret;
-
- do {
- if (buf->offset > 0)
- error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
- else
- error = GIT_EBUFS;
-
- if (error == 0)
- break; /* return the pkt */
-
- if (error < 0 && error != GIT_EBUFS)
- return error;
-
- if ((ret = gitno_recv(buf)) < 0) {
- return ret;
- } else if (ret == 0) {
- giterr_set(GITERR_NET, "early EOF");
- return GIT_EEOF;
- }
- } while (error);
-
- gitno_consume(buf, line_end);
- pkt_type = pkt->type;
- if (out != NULL)
- *out = pkt;
- else
- git__free(pkt);
-
- return pkt_type;
-}
-
-static int store_common(transport_smart *t)
-{
- git_pkt *pkt = NULL;
- gitno_buffer *buf = &t->buffer;
- int error;
-
- do {
- if ((error = recv_pkt(&pkt, buf)) < 0)
- return error;
-
- if (pkt->type == GIT_PKT_ACK) {
- if (git_vector_insert(&t->common, pkt) < 0)
- return -1;
- } else {
- git__free(pkt);
- return 0;
- }
-
- } while (1);
-
- return 0;
-}
-
-static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
-{
- git_revwalk *walk = NULL;
- git_strarray refs;
- unsigned int i;
- git_reference *ref;
- int error;
-
- if ((error = git_reference_list(&refs, repo)) < 0)
- return error;
-
- if ((error = git_revwalk_new(&walk, repo)) < 0)
- return error;
-
- git_revwalk_sorting(walk, GIT_SORT_TIME);
-
- for (i = 0; i < refs.count; ++i) {
- /* No tags */
- if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
- continue;
-
- if ((error = git_reference_lookup(&ref, repo, refs.strings[i])) < 0)
- goto on_error;
-
- if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
- continue;
-
- if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0)
- goto on_error;
-
- git_reference_free(ref);
- }
-
- git_strarray_free(&refs);
- *out = walk;
- return 0;
-
-on_error:
- git_revwalk_free(walk);
- git_reference_free(ref);
- git_strarray_free(&refs);
- return error;
-}
-
-static int wait_while_ack(gitno_buffer *buf)
-{
- int error;
- git_pkt_ack *pkt = NULL;
-
- while (1) {
- git__free(pkt);
-
- if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0)
- return error;
-
- if (pkt->type == GIT_PKT_NAK)
- break;
-
- if (pkt->type == GIT_PKT_ACK &&
- (pkt->status != GIT_ACK_CONTINUE &&
- pkt->status != GIT_ACK_COMMON)) {
- git__free(pkt);
- return 0;
- }
- }
-
- git__free(pkt);
- return 0;
-}
-
-int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
-{
- transport_smart *t = (transport_smart *)transport;
- gitno_buffer *buf = &t->buffer;
- git_buf data = GIT_BUF_INIT;
- git_revwalk *walk = NULL;
- int error = -1, pkt_type;
- unsigned int i;
- git_oid oid;
-
- if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
- return error;
-
- if ((error = fetch_setup_walk(&walk, repo)) < 0)
- goto on_error;
-
- /*
- * Our support for ACK extensions is simply to parse them. On
- * the first ACK we will accept that as enough common
- * objects. We give up if we haven't found an answer in the
- * first 256 we send.
- */
- i = 0;
- while (i < 256) {
- error = git_revwalk_next(&oid, walk);
-
- if (error < 0) {
- if (GIT_ITEROVER == error)
- break;
-
- goto on_error;
- }
-
- git_pkt_buffer_have(&oid, &data);
- i++;
- if (i % 20 == 0) {
- if (t->cancelled.val) {
- giterr_set(GITERR_NET, "The fetch was cancelled by the user");
- error = GIT_EUSER;
- goto on_error;
- }
-
- git_pkt_buffer_flush(&data);
- if (git_buf_oom(&data)) {
- error = -1;
- goto on_error;
- }
-
- if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
- goto on_error;
-
- git_buf_clear(&data);
- if (t->caps.multi_ack || t->caps.multi_ack_detailed) {
- if ((error = store_common(t)) < 0)
- goto on_error;
- } else {
- pkt_type = recv_pkt(NULL, buf);
-
- if (pkt_type == GIT_PKT_ACK) {
- break;
- } else if (pkt_type == GIT_PKT_NAK) {
- continue;
- } else if (pkt_type < 0) {
- /* recv_pkt returned an error */
- error = pkt_type;
- goto on_error;
- } else {
- giterr_set(GITERR_NET, "Unexpected pkt type");
- error = -1;
- goto on_error;
- }
- }
- }
-
- if (t->common.length > 0)
- break;
-
- if (i % 20 == 0 && t->rpc) {
- git_pkt_ack *pkt;
- unsigned int j;
-
- if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
- goto on_error;
-
- git_vector_foreach(&t->common, j, pkt) {
- if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
- goto on_error;
- }
-
- if (git_buf_oom(&data)) {
- error = -1;
- goto on_error;
- }
- }
- }
-
- /* Tell the other end that we're done negotiating */
- if (t->rpc && t->common.length > 0) {
- git_pkt_ack *pkt;
- unsigned int j;
-
- if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
- goto on_error;
-
- git_vector_foreach(&t->common, j, pkt) {
- if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
- goto on_error;
- }
-
- if (git_buf_oom(&data)) {
- error = -1;
- goto on_error;
- }
- }
-
- if ((error = git_pkt_buffer_done(&data)) < 0)
- goto on_error;
-
- if (t->cancelled.val) {
- giterr_set(GITERR_NET, "The fetch was cancelled by the user");
- error = GIT_EUSER;
- goto on_error;
- }
- if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
- goto on_error;
-
- git_buf_free(&data);
- git_revwalk_free(walk);
-
- /* Now let's eat up whatever the server gives us */
- if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
- pkt_type = recv_pkt(NULL, buf);
-
- if (pkt_type < 0) {
- return pkt_type;
- } else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
- giterr_set(GITERR_NET, "Unexpected pkt type");
- return -1;
- }
- } else {
- error = wait_while_ack(buf);
- }
-
- return error;
-
-on_error:
- git_revwalk_free(walk);
- git_buf_free(&data);
- return error;
-}
-
-static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_transfer_progress *stats)
-{
- int recvd;
-
- do {
- if (t->cancelled.val) {
- giterr_set(GITERR_NET, "The fetch was cancelled by the user");
- return GIT_EUSER;
- }
-
- if (writepack->append(writepack, buf->data, buf->offset, stats) < 0)
- return -1;
-
- gitno_consume_n(buf, buf->offset);
-
- if ((recvd = gitno_recv(buf)) < 0)
- return recvd;
- } while(recvd > 0);
-
- if (writepack->commit(writepack, stats) < 0)
- return -1;
-
- return 0;
-}
-
-struct network_packetsize_payload
-{
- git_transfer_progress_cb callback;
- void *payload;
- git_transfer_progress *stats;
- size_t last_fired_bytes;
-};
-
-static int network_packetsize(size_t received, void *payload)
-{
- struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
-
- /* Accumulate bytes */
- npp->stats->received_bytes += received;
-
- /* Fire notification if the threshold is reached */
- if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
- npp->last_fired_bytes = npp->stats->received_bytes;
-
- if (npp->callback(npp->stats, npp->payload))
- return GIT_EUSER;
- }
-
- return 0;
-}
-
-int git_smart__download_pack(
- git_transport *transport,
- git_repository *repo,
- git_transfer_progress *stats,
- git_transfer_progress_cb transfer_progress_cb,
- void *progress_payload)
-{
- transport_smart *t = (transport_smart *)transport;
- gitno_buffer *buf = &t->buffer;
- git_odb *odb;
- struct git_odb_writepack *writepack = NULL;
- int error = 0;
- struct network_packetsize_payload npp = {0};
-
- memset(stats, 0, sizeof(git_transfer_progress));
-
- if (transfer_progress_cb) {
- npp.callback = transfer_progress_cb;
- npp.payload = progress_payload;
- npp.stats = stats;
- t->packetsize_cb = &network_packetsize;
- t->packetsize_payload = &npp;
-
- /* We might have something in the buffer already from negotiate_fetch */
- if (t->buffer.offset > 0 && !t->cancelled.val)
- if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
- git_atomic_set(&t->cancelled, 1);
- }
-
- if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
- ((error = git_odb_write_pack(&writepack, odb, transfer_progress_cb, progress_payload)) != 0))
- goto done;
-
- /*
- * If the remote doesn't support the side-band, we can feed
- * the data directly to the pack writer. Otherwise, we need to
- * check which one belongs there.
- */
- if (!t->caps.side_band && !t->caps.side_band_64k) {
- error = no_sideband(t, writepack, buf, stats);
- goto done;
- }
-
- do {
- git_pkt *pkt = NULL;
-
- /* Check cancellation before network call */
- if (t->cancelled.val) {
- giterr_clear();
- error = GIT_EUSER;
- goto done;
- }
-
- if ((error = recv_pkt(&pkt, buf)) >= 0) {
- /* Check cancellation after network call */
- if (t->cancelled.val) {
- giterr_clear();
- error = GIT_EUSER;
- } else if (pkt->type == GIT_PKT_PROGRESS) {
- if (t->progress_cb) {
- git_pkt_progress *p = (git_pkt_progress *) pkt;
- error = t->progress_cb(p->data, p->len, t->message_cb_payload);
- }
- } else if (pkt->type == GIT_PKT_DATA) {
- git_pkt_data *p = (git_pkt_data *) pkt;
-
- if (p->len)
- error = writepack->append(writepack, p->data, p->len, stats);
- } else if (pkt->type == GIT_PKT_FLUSH) {
- /* A flush indicates the end of the packfile */
- git__free(pkt);
- break;
- }
- }
-
- git__free(pkt);
- if (error < 0)
- goto done;
-
- } while (1);
-
- /*
- * Trailing execution of transfer_progress_cb, if necessary...
- * Only the callback through the npp datastructure currently
- * updates the last_fired_bytes value. It is possible that
- * progress has already been reported with the correct
- * "received_bytes" value, but until (if?) this is unified
- * then we will report progress again to be sure that the
- * correct last received_bytes value is reported.
- */
- if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) {
- error = npp.callback(npp.stats, npp.payload);
- if (error != 0)
- goto done;
- }
-
- error = writepack->commit(writepack, stats);
-
-done:
- if (writepack)
- writepack->free(writepack);
- if (transfer_progress_cb) {
- t->packetsize_cb = NULL;
- t->packetsize_payload = NULL;
- }
-
- return error;
-}
-
-static int gen_pktline(git_buf *buf, git_push *push)
-{
- push_spec *spec;
- size_t i, len;
- char old_id[GIT_OID_HEXSZ+1], new_id[GIT_OID_HEXSZ+1];
-
- old_id[GIT_OID_HEXSZ] = '\0'; new_id[GIT_OID_HEXSZ] = '\0';
-
- git_vector_foreach(&push->specs, i, spec) {
- len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->refspec.dst);
-
- if (i == 0) {
- ++len; /* '\0' */
- if (push->report_status)
- len += strlen(GIT_CAP_REPORT_STATUS) + 1;
- len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
- }
-
- git_oid_fmt(old_id, &spec->roid);
- git_oid_fmt(new_id, &spec->loid);
-
- git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst);
-
- if (i == 0) {
- git_buf_putc(buf, '\0');
- /* Core git always starts their capabilities string with a space */
- if (push->report_status) {
- git_buf_putc(buf, ' ');
- git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
- }
- git_buf_putc(buf, ' ');
- git_buf_printf(buf, GIT_CAP_SIDE_BAND_64K);
- }
-
- git_buf_putc(buf, '\n');
- }
-
- git_buf_puts(buf, "0000");
- return git_buf_oom(buf) ? -1 : 0;
-}
-
-static int add_push_report_pkt(git_push *push, git_pkt *pkt)
-{
- push_status *status;
-
- switch (pkt->type) {
- case GIT_PKT_OK:
- status = git__calloc(1, sizeof(push_status));
- GITERR_CHECK_ALLOC(status);
- status->msg = NULL;
- status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
- if (!status->ref ||
- git_vector_insert(&push->status, status) < 0) {
- git_push_status_free(status);
- return -1;
- }
- break;
- case GIT_PKT_NG:
- status = git__calloc(1, sizeof(push_status));
- GITERR_CHECK_ALLOC(status);
- status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
- status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
- if (!status->ref || !status->msg ||
- git_vector_insert(&push->status, status) < 0) {
- git_push_status_free(status);
- return -1;
- }
- break;
- case GIT_PKT_UNPACK:
- push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
- break;
- case GIT_PKT_FLUSH:
- return GIT_ITEROVER;
- default:
- giterr_set(GITERR_NET, "report-status: protocol error");
- return -1;
- }
-
- return 0;
-}
-
-static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_buf *data_pkt_buf)
-{
- git_pkt *pkt;
- const char *line, *line_end = NULL;
- size_t line_len;
- int error;
- int reading_from_buf = data_pkt_buf->size > 0;
-
- if (reading_from_buf) {
- /* We had an existing partial packet, so add the new
- * packet to the buffer and parse the whole thing */
- git_buf_put(data_pkt_buf, data_pkt->data, data_pkt->len);
- line = data_pkt_buf->ptr;
- line_len = data_pkt_buf->size;
- }
- else {
- line = data_pkt->data;
- line_len = data_pkt->len;
- }
-
- while (line_len > 0) {
- error = git_pkt_parse_line(&pkt, line, &line_end, line_len);
-
- if (error == GIT_EBUFS) {
- /* Buffer the data when the inner packet is split
- * across multiple sideband packets */
- if (!reading_from_buf)
- git_buf_put(data_pkt_buf, line, line_len);
- error = 0;
- goto done;
- }
- else if (error < 0)
- goto done;
-
- /* Advance in the buffer */
- line_len -= (line_end - line);
- line = line_end;
-
- /* When a valid packet with no content has been
- * read, git_pkt_parse_line does not report an
- * error, but the pkt pointer has not been set.
- * Handle this by skipping over empty packets.
- */
- if (pkt == NULL)
- continue;
-
- error = add_push_report_pkt(push, pkt);
-
- git_pkt_free(pkt);
-
- if (error < 0 && error != GIT_ITEROVER)
- goto done;
- }
-
- error = 0;
-
-done:
- if (reading_from_buf)
- git_buf_consume(data_pkt_buf, line_end);
- return error;
-}
-
-static int parse_report(transport_smart *transport, git_push *push)
-{
- git_pkt *pkt = NULL;
- const char *line_end = NULL;
- gitno_buffer *buf = &transport->buffer;
- int error, recvd;
- git_buf data_pkt_buf = GIT_BUF_INIT;
-
- for (;;) {
- if (buf->offset > 0)
- error = git_pkt_parse_line(&pkt, buf->data,
- &line_end, buf->offset);
- else
- error = GIT_EBUFS;
-
- if (error < 0 && error != GIT_EBUFS) {
- error = -1;
- goto done;
- }
-
- if (error == GIT_EBUFS) {
- if ((recvd = gitno_recv(buf)) < 0) {
- error = recvd;
- goto done;
- }
-
- if (recvd == 0) {
- giterr_set(GITERR_NET, "early EOF");
- error = GIT_EEOF;
- goto done;
- }
- continue;
- }
-
- gitno_consume(buf, line_end);
-
- error = 0;
-
- if (pkt == NULL)
- continue;
-
- switch (pkt->type) {
- case GIT_PKT_DATA:
- /* This is a sideband packet which contains other packets */
- error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt, &data_pkt_buf);
- break;
- case GIT_PKT_ERR:
- giterr_set(GITERR_NET, "report-status: Error reported: %s",
- ((git_pkt_err *)pkt)->error);
- error = -1;
- break;
- case GIT_PKT_PROGRESS:
- if (transport->progress_cb) {
- git_pkt_progress *p = (git_pkt_progress *) pkt;
- error = transport->progress_cb(p->data, p->len, transport->message_cb_payload);
- }
- break;
- default:
- error = add_push_report_pkt(push, pkt);
- break;
- }
-
- git_pkt_free(pkt);
-
- /* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
- if (error == GIT_ITEROVER) {
- error = 0;
- if (data_pkt_buf.size > 0) {
- /* If there was data remaining in the pack data buffer,
- * then the server sent a partial pkt-line */
- giterr_set(GITERR_NET, "Incomplete pack data pkt-line");
- error = GIT_ERROR;
- }
- goto done;
- }
-
- if (error < 0) {
- goto done;
- }
- }
-done:
- git_buf_free(&data_pkt_buf);
- return error;
-}
-
-static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
-{
- git_pkt_ref *added = git__calloc(1, sizeof(git_pkt_ref));
- GITERR_CHECK_ALLOC(added);
-
- added->type = GIT_PKT_REF;
- git_oid_cpy(&added->head.oid, &push_spec->loid);
- added->head.name = git__strdup(push_spec->refspec.dst);
-
- if (!added->head.name ||
- git_vector_insert(refs, added) < 0) {
- git_pkt_free((git_pkt *)added);
- return -1;
- }
-
- return 0;
-}
-
-static int update_refs_from_report(
- git_vector *refs,
- git_vector *push_specs,
- git_vector *push_report)
-{
- git_pkt_ref *ref;
- push_spec *push_spec;
- push_status *push_status;
- size_t i, j, refs_len;
- int cmp;
-
- /* For each push spec we sent to the server, we should have
- * gotten back a status packet in the push report */
- if (push_specs->length != push_report->length) {
- giterr_set(GITERR_NET, "report-status: protocol error");
- return -1;
- }
-
- /* We require that push_specs be sorted with push_spec_rref_cmp,
- * and that push_report be sorted with push_status_ref_cmp */
- git_vector_sort(push_specs);
- git_vector_sort(push_report);
-
- git_vector_foreach(push_specs, i, push_spec) {
- push_status = git_vector_get(push_report, i);
-
- /* For each push spec we sent to the server, we should have
- * gotten back a status packet in the push report which matches */
- if (strcmp(push_spec->refspec.dst, push_status->ref)) {
- giterr_set(GITERR_NET, "report-status: protocol error");
- return -1;
- }
- }
-
- /* We require that refs be sorted with ref_name_cmp */
- git_vector_sort(refs);
- i = j = 0;
- refs_len = refs->length;
-
- /* Merge join push_specs with refs */
- while (i < push_specs->length && j < refs_len) {
- push_spec = git_vector_get(push_specs, i);
- push_status = git_vector_get(push_report, i);
- ref = git_vector_get(refs, j);
-
- cmp = strcmp(push_spec->refspec.dst, ref->head.name);
-
- /* Iterate appropriately */
- if (cmp <= 0) i++;
- if (cmp >= 0) j++;
-
- /* Add case */
- if (cmp < 0 &&
- !push_status->msg &&
- add_ref_from_push_spec(refs, push_spec) < 0)
- return -1;
-
- /* Update case, delete case */
- if (cmp == 0 &&
- !push_status->msg)
- git_oid_cpy(&ref->head.oid, &push_spec->loid);
- }
-
- for (; i < push_specs->length; i++) {
- push_spec = git_vector_get(push_specs, i);
- push_status = git_vector_get(push_report, i);
-
- /* Add case */
- if (!push_status->msg &&
- add_ref_from_push_spec(refs, push_spec) < 0)
- return -1;
- }
-
- /* Remove any refs which we updated to have a zero OID. */
- git_vector_rforeach(refs, i, ref) {
- if (git_oid_iszero(&ref->head.oid)) {
- git_vector_remove(refs, i);
- git_pkt_free((git_pkt *)ref);
- }
- }
-
- git_vector_sort(refs);
-
- return 0;
-}
-
-struct push_packbuilder_payload
-{
- git_smart_subtransport_stream *stream;
- git_packbuilder *pb;
- git_push_transfer_progress cb;
- void *cb_payload;
- size_t last_bytes;
- double last_progress_report_time;
-};
-
-static int stream_thunk(void *buf, size_t size, void *data)
-{
- int error = 0;
- struct push_packbuilder_payload *payload = data;
-
- if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
- return error;
-
- if (payload->cb) {
- double current_time = git__timer();
- payload->last_bytes += size;
-
- if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
- payload->last_progress_report_time = current_time;
- error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
- }
- }
-
- return error;
-}
-
-int git_smart__push(git_transport *transport, git_push *push, const git_remote_callbacks *cbs)
-{
- transport_smart *t = (transport_smart *)transport;
- struct push_packbuilder_payload packbuilder_payload = {0};
- git_buf pktline = GIT_BUF_INIT;
- int error = 0, need_pack = 0;
- push_spec *spec;
- unsigned int i;
-
- packbuilder_payload.pb = push->pb;
-
- if (cbs && cbs->push_transfer_progress) {
- packbuilder_payload.cb = cbs->push_transfer_progress;
- packbuilder_payload.cb_payload = cbs->payload;
- }
-
-#ifdef PUSH_DEBUG
-{
- git_remote_head *head;
- char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0';
-
- git_vector_foreach(&push->remote->refs, i, head) {
- git_oid_fmt(hex, &head->oid);
- fprintf(stderr, "%s (%s)\n", hex, head->name);
- }
-
- git_vector_foreach(&push->specs, i, spec) {
- git_oid_fmt(hex, &spec->roid);
- fprintf(stderr, "%s (%s) -> ", hex, spec->lref);
- git_oid_fmt(hex, &spec->loid);
- fprintf(stderr, "%s (%s)\n", hex, spec->rref ?
- spec->rref : spec->lref);
- }
-}
-#endif
-
- /*
- * Figure out if we need to send a packfile; which is in all
- * cases except when we only send delete commands
- */
- git_vector_foreach(&push->specs, i, spec) {
- if (spec->refspec.src && spec->refspec.src[0] != '\0') {
- need_pack = 1;
- break;
- }
- }
-
- if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
- (error = gen_pktline(&pktline, push)) < 0 ||
- (error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0)
- goto done;
-
- if (need_pack &&
- (error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
- goto done;
-
- /* If we sent nothing or the server doesn't support report-status, then
- * we consider the pack to have been unpacked successfully */
- if (!push->specs.length || !push->report_status)
- push->unpack_ok = 1;
- else if ((error = parse_report(t, push)) < 0)
- goto done;
-
- /* If progress is being reported write the final report */
- if (cbs && cbs->push_transfer_progress) {
- error = cbs->push_transfer_progress(
- push->pb->nr_written,
- push->pb->nr_objects,
- packbuilder_payload.last_bytes,
- cbs->payload);
-
- if (error < 0)
- goto done;
- }
-
- if (push->status.length) {
- error = update_refs_from_report(&t->refs, &push->specs, &push->status);
- if (error < 0)
- goto done;
-
- error = git_smart__update_heads(t, NULL);
- }
-
-done:
- git_buf_free(&pktline);
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifdef GIT_SSH
-#include <libssh2.h>
-#endif
-
-#include "git2.h"
-#include "buffer.h"
-#include "netops.h"
-#include "smart.h"
-#include "cred.h"
-#include "socket_stream.h"
-#include "ssh.h"
-
-#ifdef GIT_SSH
-
-#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
-
-static const char *ssh_prefixes[] = { "ssh://", "ssh+git://", "git+ssh://" };
-
-static const char cmd_uploadpack[] = "git-upload-pack";
-static const char cmd_receivepack[] = "git-receive-pack";
-
-typedef struct {
- git_smart_subtransport_stream parent;
- git_stream *io;
- LIBSSH2_SESSION *session;
- LIBSSH2_CHANNEL *channel;
- const char *cmd;
- char *url;
- unsigned sent_command : 1;
-} ssh_stream;
-
-typedef struct {
- git_smart_subtransport parent;
- transport_smart *owner;
- ssh_stream *current_stream;
- git_cred *cred;
- char *cmd_uploadpack;
- char *cmd_receivepack;
-} ssh_subtransport;
-
-static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username);
-
-static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
-{
- char *ssherr;
- libssh2_session_last_error(session, &ssherr, NULL, 0);
-
- giterr_set(GITERR_SSH, "%s: %s", errmsg, ssherr);
-}
-
-/*
- * Create a git protocol request.
- *
- * For example: git-upload-pack '/libgit2/libgit2'
- */
-static int gen_proto(git_buf *request, const char *cmd, const char *url)
-{
- char *repo;
- int len;
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
- const char *p = ssh_prefixes[i];
-
- if (!git__prefixcmp(url, p)) {
- url = url + strlen(p);
- repo = strchr(url, '/');
- if (repo && repo[1] == '~')
- ++repo;
-
- goto done;
- }
- }
- repo = strchr(url, ':');
- if (repo) repo++;
-
-done:
- if (!repo) {
- giterr_set(GITERR_NET, "Malformed git protocol URL");
- return -1;
- }
-
- len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1;
-
- git_buf_grow(request, len);
- git_buf_printf(request, "%s '%s'", cmd, repo);
- git_buf_putc(request, '\0');
-
- if (git_buf_oom(request))
- return -1;
-
- return 0;
-}
-
-static int send_command(ssh_stream *s)
-{
- int error;
- git_buf request = GIT_BUF_INIT;
-
- error = gen_proto(&request, s->cmd, s->url);
- if (error < 0)
- goto cleanup;
-
- error = libssh2_channel_exec(s->channel, request.ptr);
- if (error < LIBSSH2_ERROR_NONE) {
- ssh_error(s->session, "SSH could not execute request");
- goto cleanup;
- }
-
- s->sent_command = 1;
-
-cleanup:
- git_buf_free(&request);
- return error;
-}
-
-static int ssh_stream_read(
- git_smart_subtransport_stream *stream,
- char *buffer,
- size_t buf_size,
- size_t *bytes_read)
-{
- int rc;
- ssh_stream *s = (ssh_stream *)stream;
-
- *bytes_read = 0;
-
- if (!s->sent_command && send_command(s) < 0)
- return -1;
-
- if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < LIBSSH2_ERROR_NONE) {
- ssh_error(s->session, "SSH could not read data");
- return -1;
- }
-
- /*
- * If we can't get anything out of stdout, it's typically a
- * not-found error, so read from stderr and signal EOF on
- * stderr.
- */
- if (rc == 0) {
- if ((rc = libssh2_channel_read_stderr(s->channel, buffer, buf_size)) > 0) {
- giterr_set(GITERR_SSH, "%*s", rc, buffer);
- return GIT_EEOF;
- } else if (rc < LIBSSH2_ERROR_NONE) {
- ssh_error(s->session, "SSH could not read stderr");
- return -1;
- }
- }
-
-
- *bytes_read = rc;
-
- return 0;
-}
-
-static int ssh_stream_write(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len)
-{
- ssh_stream *s = (ssh_stream *)stream;
- size_t off = 0;
- ssize_t ret = 0;
-
- if (!s->sent_command && send_command(s) < 0)
- return -1;
-
- do {
- ret = libssh2_channel_write(s->channel, buffer + off, len - off);
- if (ret < 0)
- break;
-
- off += ret;
-
- } while (off < len);
-
- if (ret < 0) {
- ssh_error(s->session, "SSH could not write data");
- return -1;
- }
-
- return 0;
-}
-
-static void ssh_stream_free(git_smart_subtransport_stream *stream)
-{
- ssh_stream *s = (ssh_stream *)stream;
- ssh_subtransport *t;
-
- if (!stream)
- return;
-
- t = OWNING_SUBTRANSPORT(s);
- t->current_stream = NULL;
-
- if (s->channel) {
- libssh2_channel_close(s->channel);
- libssh2_channel_free(s->channel);
- s->channel = NULL;
- }
-
- if (s->session) {
- libssh2_session_free(s->session);
- s->session = NULL;
- }
-
- if (s->io) {
- git_stream_close(s->io);
- git_stream_free(s->io);
- s->io = NULL;
- }
-
- git__free(s->url);
- git__free(s);
-}
-
-static int ssh_stream_alloc(
- ssh_subtransport *t,
- const char *url,
- const char *cmd,
- git_smart_subtransport_stream **stream)
-{
- ssh_stream *s;
-
- assert(stream);
-
- s = git__calloc(sizeof(ssh_stream), 1);
- GITERR_CHECK_ALLOC(s);
-
- s->parent.subtransport = &t->parent;
- s->parent.read = ssh_stream_read;
- s->parent.write = ssh_stream_write;
- s->parent.free = ssh_stream_free;
-
- s->cmd = cmd;
-
- s->url = git__strdup(url);
- if (!s->url) {
- git__free(s);
- return -1;
- }
-
- *stream = &s->parent;
- return 0;
-}
-
-static int git_ssh_extract_url_parts(
- char **host,
- char **username,
- const char *url)
-{
- char *colon, *at;
- const char *start;
-
- colon = strchr(url, ':');
-
-
- at = strchr(url, '@');
- if (at) {
- start = at + 1;
- *username = git__substrdup(url, at - url);
- GITERR_CHECK_ALLOC(*username);
- } else {
- start = url;
- *username = NULL;
- }
-
- if (colon == NULL || (colon < start)) {
- giterr_set(GITERR_NET, "Malformed URL");
- return -1;
- }
-
- *host = git__substrdup(start, colon - start);
- GITERR_CHECK_ALLOC(*host);
-
- return 0;
-}
-
-static int ssh_agent_auth(LIBSSH2_SESSION *session, git_cred_ssh_key *c) {
- int rc = LIBSSH2_ERROR_NONE;
-
- struct libssh2_agent_publickey *curr, *prev = NULL;
-
- LIBSSH2_AGENT *agent = libssh2_agent_init(session);
-
- if (agent == NULL)
- return -1;
-
- rc = libssh2_agent_connect(agent);
-
- if (rc != LIBSSH2_ERROR_NONE)
- goto shutdown;
-
- rc = libssh2_agent_list_identities(agent);
-
- if (rc != LIBSSH2_ERROR_NONE)
- goto shutdown;
-
- while (1) {
- rc = libssh2_agent_get_identity(agent, &curr, prev);
-
- if (rc < 0)
- goto shutdown;
-
- /* rc is set to 1 whenever the ssh agent ran out of keys to check.
- * Set the error code to authentication failure rather than erroring
- * out with an untranslatable error code.
- */
- if (rc == 1) {
- rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
- goto shutdown;
- }
-
- rc = libssh2_agent_userauth(agent, c->username, curr);
-
- if (rc == 0)
- break;
-
- prev = curr;
- }
-
-shutdown:
-
- if (rc != LIBSSH2_ERROR_NONE)
- ssh_error(session, "error authenticating");
-
- libssh2_agent_disconnect(agent);
- libssh2_agent_free(agent);
-
- return rc;
-}
-
-static int _git_ssh_authenticate_session(
- LIBSSH2_SESSION* session,
- git_cred* cred)
-{
- int rc;
-
- do {
- giterr_clear();
- switch (cred->credtype) {
- case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
- git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
- rc = libssh2_userauth_password(session, c->username, c->password);
- break;
- }
- case GIT_CREDTYPE_SSH_KEY: {
- git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
-
- if (c->privatekey)
- rc = libssh2_userauth_publickey_fromfile(
- session, c->username, c->publickey,
- c->privatekey, c->passphrase);
- else
- rc = ssh_agent_auth(session, c);
-
- break;
- }
- case GIT_CREDTYPE_SSH_CUSTOM: {
- git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
-
- rc = libssh2_userauth_publickey(
- session, c->username, (const unsigned char *)c->publickey,
- c->publickey_len, c->sign_callback, &c->payload);
- break;
- }
- case GIT_CREDTYPE_SSH_INTERACTIVE: {
- void **abstract = libssh2_session_abstract(session);
- git_cred_ssh_interactive *c = (git_cred_ssh_interactive *)cred;
-
- /* ideally, we should be able to set this by calling
- * libssh2_session_init_ex() instead of libssh2_session_init().
- * libssh2's API is inconsistent here i.e. libssh2_userauth_publickey()
- * allows you to pass the `abstract` as part of the call, whereas
- * libssh2_userauth_keyboard_interactive() does not!
- *
- * The only way to set the `abstract` pointer is by calling
- * libssh2_session_abstract(), which will replace the existing
- * pointer as is done below. This is safe for now (at time of writing),
- * but may not be valid in future.
- */
- *abstract = c->payload;
-
- rc = libssh2_userauth_keyboard_interactive(
- session, c->username, c->prompt_callback);
- break;
- }
-#ifdef GIT_SSH_MEMORY_CREDENTIALS
- case GIT_CREDTYPE_SSH_MEMORY: {
- git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
-
- assert(c->username);
- assert(c->privatekey);
-
- rc = libssh2_userauth_publickey_frommemory(
- session,
- c->username,
- strlen(c->username),
- c->publickey,
- c->publickey ? strlen(c->publickey) : 0,
- c->privatekey,
- strlen(c->privatekey),
- c->passphrase);
- break;
- }
-#endif
- default:
- rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
- }
- } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
-
- if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED || rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED)
- return GIT_EAUTH;
-
- if (rc != LIBSSH2_ERROR_NONE) {
- if (!giterr_last())
- ssh_error(session, "Failed to authenticate SSH session");
- return -1;
- }
-
- return 0;
-}
-
-static int request_creds(git_cred **out, ssh_subtransport *t, const char *user, int auth_methods)
-{
- int error, no_callback = 0;
- git_cred *cred = NULL;
-
- if (!t->owner->cred_acquire_cb) {
- no_callback = 1;
- } else {
- error = t->owner->cred_acquire_cb(&cred, t->owner->url, user, auth_methods,
- t->owner->cred_acquire_payload);
-
- if (error == GIT_PASSTHROUGH)
- no_callback = 1;
- else if (error < 0)
- return error;
- else if (!cred) {
- giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
- return -1;
- }
- }
-
- if (no_callback) {
- giterr_set(GITERR_SSH, "authentication required but no callback set");
- return -1;
- }
-
- if (!(cred->credtype & auth_methods)) {
- cred->free(cred);
- giterr_set(GITERR_SSH, "callback returned unsupported credentials type");
- return -1;
- }
-
- *out = cred;
-
- return 0;
-}
-
-static int _git_ssh_session_create(
- LIBSSH2_SESSION** session,
- git_stream *io)
-{
- int rc = 0;
- LIBSSH2_SESSION* s;
- git_socket_stream *socket = (git_socket_stream *) io;
-
- assert(session);
-
- s = libssh2_session_init();
- if (!s) {
- giterr_set(GITERR_NET, "Failed to initialize SSH session");
- return -1;
- }
-
- do {
- rc = libssh2_session_startup(s, socket->s);
- } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
-
- if (rc != LIBSSH2_ERROR_NONE) {
- ssh_error(s, "Failed to start SSH session");
- libssh2_session_free(s);
- return -1;
- }
-
- libssh2_session_set_blocking(s, 1);
-
- *session = s;
-
- return 0;
-}
-
-static int _git_ssh_setup_conn(
- ssh_subtransport *t,
- const char *url,
- const char *cmd,
- git_smart_subtransport_stream **stream)
-{
- char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
- const char *default_port="22";
- int auth_methods, error = 0;
- size_t i;
- ssh_stream *s;
- git_cred *cred = NULL;
- LIBSSH2_SESSION* session=NULL;
- LIBSSH2_CHANNEL* channel=NULL;
-
- t->current_stream = NULL;
-
- *stream = NULL;
- if (ssh_stream_alloc(t, url, cmd, stream) < 0)
- return -1;
-
- s = (ssh_stream *)*stream;
- s->session = NULL;
- s->channel = NULL;
-
- for (i = 0; i < ARRAY_SIZE(ssh_prefixes); ++i) {
- const char *p = ssh_prefixes[i];
-
- if (!git__prefixcmp(url, p)) {
- if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port)) < 0)
- goto done;
-
- goto post_extract;
- }
- }
- if ((error = git_ssh_extract_url_parts(&host, &user, url)) < 0)
- goto done;
- port = git__strdup(default_port);
- GITERR_CHECK_ALLOC(port);
-
-post_extract:
- if ((error = git_socket_stream_new(&s->io, host, port)) < 0 ||
- (error = git_stream_connect(s->io)) < 0)
- goto done;
-
- if ((error = _git_ssh_session_create(&session, s->io)) < 0)
- goto done;
-
- if (t->owner->certificate_check_cb != NULL) {
- git_cert_hostkey cert = {{ 0 }}, *cert_ptr;
- const char *key;
-
- cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
-
- key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
- if (key != NULL) {
- cert.type |= GIT_CERT_SSH_SHA1;
- memcpy(&cert.hash_sha1, key, 20);
- }
-
- key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
- if (key != NULL) {
- cert.type |= GIT_CERT_SSH_MD5;
- memcpy(&cert.hash_md5, key, 16);
- }
-
- if (cert.type == 0) {
- giterr_set(GITERR_SSH, "unable to get the host key");
- error = -1;
- goto done;
- }
-
- /* We don't currently trust any hostkeys */
- giterr_clear();
-
- cert_ptr = &cert;
-
- error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, host, t->owner->message_cb_payload);
- if (error < 0) {
- if (!giterr_last())
- giterr_set(GITERR_NET, "user cancelled hostkey check");
-
- goto done;
- }
- }
-
- /* we need the username to ask for auth methods */
- if (!user) {
- if ((error = request_creds(&cred, t, NULL, GIT_CREDTYPE_USERNAME)) < 0)
- goto done;
-
- user = git__strdup(((git_cred_username *) cred)->username);
- cred->free(cred);
- cred = NULL;
- if (!user)
- goto done;
- } else if (user && pass) {
- if ((error = git_cred_userpass_plaintext_new(&cred, user, pass)) < 0)
- goto done;
- }
-
- if ((error = list_auth_methods(&auth_methods, session, user)) < 0)
- goto done;
-
- error = GIT_EAUTH;
- /* if we already have something to try */
- if (cred && auth_methods & cred->credtype)
- error = _git_ssh_authenticate_session(session, cred);
-
- while (error == GIT_EAUTH) {
- if (cred) {
- cred->free(cred);
- cred = NULL;
- }
-
- if ((error = request_creds(&cred, t, user, auth_methods)) < 0)
- goto done;
-
- if (strcmp(user, git_cred__username(cred))) {
- giterr_set(GITERR_SSH, "username does not match previous request");
- error = -1;
- goto done;
- }
-
- error = _git_ssh_authenticate_session(session, cred);
- }
-
- if (error < 0)
- goto done;
-
- channel = libssh2_channel_open_session(session);
- if (!channel) {
- error = -1;
- ssh_error(session, "Failed to open SSH channel");
- goto done;
- }
-
- libssh2_channel_set_blocking(channel, 1);
-
- s->session = session;
- s->channel = channel;
-
- t->current_stream = s;
-
-done:
- if (error < 0) {
- ssh_stream_free(*stream);
-
- if (session)
- libssh2_session_free(session);
- }
-
- if (cred)
- cred->free(cred);
-
- git__free(host);
- git__free(port);
- git__free(path);
- git__free(user);
- git__free(pass);
-
- return error;
-}
-
-static int ssh_uploadpack_ls(
- ssh_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack;
-
- return _git_ssh_setup_conn(t, url, cmd, stream);
-}
-
-static int ssh_uploadpack(
- ssh_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- GIT_UNUSED(url);
-
- if (t->current_stream) {
- *stream = &t->current_stream->parent;
- return 0;
- }
-
- giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK");
- return -1;
-}
-
-static int ssh_receivepack_ls(
- ssh_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- const char *cmd = t->cmd_receivepack ? t->cmd_receivepack : cmd_receivepack;
-
-
- return _git_ssh_setup_conn(t, url, cmd, stream);
-}
-
-static int ssh_receivepack(
- ssh_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- GIT_UNUSED(url);
-
- if (t->current_stream) {
- *stream = &t->current_stream->parent;
- return 0;
- }
-
- giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK");
- return -1;
-}
-
-static int _ssh_action(
- git_smart_subtransport_stream **stream,
- git_smart_subtransport *subtransport,
- const char *url,
- git_smart_service_t action)
-{
- ssh_subtransport *t = (ssh_subtransport *) subtransport;
-
- switch (action) {
- case GIT_SERVICE_UPLOADPACK_LS:
- return ssh_uploadpack_ls(t, url, stream);
-
- case GIT_SERVICE_UPLOADPACK:
- return ssh_uploadpack(t, url, stream);
-
- case GIT_SERVICE_RECEIVEPACK_LS:
- return ssh_receivepack_ls(t, url, stream);
-
- case GIT_SERVICE_RECEIVEPACK:
- return ssh_receivepack(t, url, stream);
- }
-
- *stream = NULL;
- return -1;
-}
-
-static int _ssh_close(git_smart_subtransport *subtransport)
-{
- ssh_subtransport *t = (ssh_subtransport *) subtransport;
-
- assert(!t->current_stream);
-
- GIT_UNUSED(t);
-
- return 0;
-}
-
-static void _ssh_free(git_smart_subtransport *subtransport)
-{
- ssh_subtransport *t = (ssh_subtransport *) subtransport;
-
- assert(!t->current_stream);
-
- git__free(t->cmd_uploadpack);
- git__free(t->cmd_receivepack);
- git__free(t);
-}
-
-#define SSH_AUTH_PUBLICKEY "publickey"
-#define SSH_AUTH_PASSWORD "password"
-#define SSH_AUTH_KEYBOARD_INTERACTIVE "keyboard-interactive"
-
-static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username)
-{
- const char *list, *ptr;
-
- *out = 0;
-
- list = libssh2_userauth_list(session, username, strlen(username));
-
- /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
- if (list == NULL && !libssh2_userauth_authenticated(session)) {
- ssh_error(session, "Failed to retrieve list of SSH authentication methods");
- return -1;
- }
-
- ptr = list;
- while (ptr) {
- if (*ptr == ',')
- ptr++;
-
- if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) {
- *out |= GIT_CREDTYPE_SSH_KEY;
- *out |= GIT_CREDTYPE_SSH_CUSTOM;
-#ifdef GIT_SSH_MEMORY_CREDENTIALS
- *out |= GIT_CREDTYPE_SSH_MEMORY;
-#endif
- ptr += strlen(SSH_AUTH_PUBLICKEY);
- continue;
- }
-
- if (!git__prefixcmp(ptr, SSH_AUTH_PASSWORD)) {
- *out |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
- ptr += strlen(SSH_AUTH_PASSWORD);
- continue;
- }
-
- if (!git__prefixcmp(ptr, SSH_AUTH_KEYBOARD_INTERACTIVE)) {
- *out |= GIT_CREDTYPE_SSH_INTERACTIVE;
- ptr += strlen(SSH_AUTH_KEYBOARD_INTERACTIVE);
- continue;
- }
-
- /* Skipt it if we don't know it */
- ptr = strchr(ptr, ',');
- }
-
- return 0;
-}
-#endif
-
-int git_smart_subtransport_ssh(
- git_smart_subtransport **out, git_transport *owner, void *param)
-{
-#ifdef GIT_SSH
- ssh_subtransport *t;
-
- assert(out);
-
- GIT_UNUSED(param);
-
- t = git__calloc(sizeof(ssh_subtransport), 1);
- GITERR_CHECK_ALLOC(t);
-
- t->owner = (transport_smart *)owner;
- t->parent.action = _ssh_action;
- t->parent.close = _ssh_close;
- t->parent.free = _ssh_free;
-
- *out = (git_smart_subtransport *) t;
- return 0;
-#else
- GIT_UNUSED(owner);
- GIT_UNUSED(param);
-
- assert(out);
- *out = NULL;
-
- giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support");
- return -1;
-#endif
-}
-
-int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload)
-{
-#ifdef GIT_SSH
- git_strarray *paths = (git_strarray *) payload;
- git_transport *transport;
- transport_smart *smart;
- ssh_subtransport *t;
- int error;
- git_smart_subtransport_definition ssh_definition = {
- git_smart_subtransport_ssh,
- 0, /* no RPC */
- NULL,
- };
-
- if (paths->count != 2) {
- giterr_set(GITERR_SSH, "invalid ssh paths, must be two strings");
- return GIT_EINVALIDSPEC;
- }
-
- if ((error = git_transport_smart(&transport, owner, &ssh_definition)) < 0)
- return error;
-
- smart = (transport_smart *) transport;
- t = (ssh_subtransport *) smart->wrapped;
-
- t->cmd_uploadpack = git__strdup(paths->strings[0]);
- GITERR_CHECK_ALLOC(t->cmd_uploadpack);
- t->cmd_receivepack = git__strdup(paths->strings[1]);
- GITERR_CHECK_ALLOC(t->cmd_receivepack);
-
- *out = transport;
- return 0;
-#else
- GIT_UNUSED(owner);
- GIT_UNUSED(payload);
-
- assert(out);
- *out = NULL;
-
- giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support");
- return -1;
-#endif
-}
-
-int git_transport_ssh_global_init(void)
-{
-#ifdef GIT_SSH
-
- libssh2_init(0);
- return 0;
-
-#else
-
- /* Nothing to initialize */
- return 0;
-
-#endif
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_ssh_h__
-#define INCLUDE_ssh_h__
-
-int git_transport_ssh_global_init(void);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifdef GIT_WINHTTP
-
-#include "git2.h"
-#include "git2/transport.h"
-#include "buffer.h"
-#include "posix.h"
-#include "netops.h"
-#include "smart.h"
-#include "remote.h"
-#include "repository.h"
-#include "global.h"
-
-#include <wincrypt.h>
-#include <winhttp.h>
-
-/* For IInternetSecurityManager zone check */
-#include <objbase.h>
-#include <urlmon.h>
-
-#define WIDEN2(s) L ## s
-#define WIDEN(s) WIDEN2(s)
-
-#define MAX_CONTENT_TYPE_LEN 100
-#define WINHTTP_OPTION_PEERDIST_EXTENSION_STATE 109
-#define CACHED_POST_BODY_BUF_SIZE 4096
-#define UUID_LENGTH_CCH 32
-#define TIMEOUT_INFINITE -1
-#define DEFAULT_CONNECT_TIMEOUT 60000
-#ifndef WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH
-#define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0
-#endif
-
-static const char *prefix_https = "https://";
-static const char *upload_pack_service = "upload-pack";
-static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
-static const char *upload_pack_service_url = "/git-upload-pack";
-static const char *receive_pack_service = "receive-pack";
-static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack";
-static const char *receive_pack_service_url = "/git-receive-pack";
-static const wchar_t *get_verb = L"GET";
-static const wchar_t *post_verb = L"POST";
-static const wchar_t *pragma_nocache = L"Pragma: no-cache";
-static const wchar_t *transfer_encoding = L"Transfer-Encoding: chunked";
-static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
- SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
- SECURITY_FLAG_IGNORE_UNKNOWN_CA;
-
-#if defined(__MINGW32__)
-static const CLSID CLSID_InternetSecurityManager_mingw =
- { 0x7B8A2D94, 0x0AC9, 0x11D1,
- { 0x89, 0x6C, 0x00, 0xC0, 0x4F, 0xB6, 0xBF, 0xC4 } };
-static const IID IID_IInternetSecurityManager_mingw =
- { 0x79EAC9EE, 0xBAF9, 0x11CE,
- { 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B } };
-
-# define CLSID_InternetSecurityManager CLSID_InternetSecurityManager_mingw
-# define IID_IInternetSecurityManager IID_IInternetSecurityManager_mingw
-#endif
-
-#define OWNING_SUBTRANSPORT(s) ((winhttp_subtransport *)(s)->parent.subtransport)
-
-typedef enum {
- GIT_WINHTTP_AUTH_BASIC = 1,
- GIT_WINHTTP_AUTH_NEGOTIATE = 2,
-} winhttp_authmechanism_t;
-
-typedef struct {
- git_smart_subtransport_stream parent;
- const char *service;
- const char *service_url;
- const wchar_t *verb;
- HINTERNET request;
- wchar_t *request_uri;
- char *chunk_buffer;
- unsigned chunk_buffer_len;
- HANDLE post_body;
- DWORD post_body_len;
- unsigned sent_request : 1,
- received_response : 1,
- chunked : 1;
-} winhttp_stream;
-
-typedef struct {
- git_smart_subtransport parent;
- transport_smart *owner;
- gitno_connection_data connection_data;
- gitno_connection_data proxy_connection_data;
- git_cred *cred;
- git_cred *url_cred;
- git_cred *proxy_cred;
- int auth_mechanism;
- HINTERNET session;
- HINTERNET connection;
-} winhttp_subtransport;
-
-static int apply_basic_credential_proxy(HINTERNET request, git_cred *cred)
-{
- git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
- wchar_t *user, *pass;
- int error;
-
- if ((error = git__utf8_to_16_alloc(&user, c->username)) < 0)
- return error;
-
- if ((error = git__utf8_to_16_alloc(&pass, c->password)) < 0)
- return error;
-
- if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_PROXY, WINHTTP_AUTH_SCHEME_BASIC,
- user, pass, NULL)) {
- giterr_set(GITERR_OS, "failed to set proxy auth");
- error = -1;
- }
-
- git__free(user);
- git__free(pass);
-
- return error;
-}
-
-static int apply_basic_credential(HINTERNET request, git_cred *cred)
-{
- git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
- git_buf buf = GIT_BUF_INIT, raw = GIT_BUF_INIT;
- wchar_t *wide = NULL;
- int error = -1, wide_len;
-
- git_buf_printf(&raw, "%s:%s", c->username, c->password);
-
- if (git_buf_oom(&raw) ||
- git_buf_puts(&buf, "Authorization: Basic ") < 0 ||
- git_buf_encode_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
- goto on_error;
-
- if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) {
- giterr_set(GITERR_OS, "Failed to convert string to wide form");
- goto on_error;
- }
-
- if (!WinHttpAddRequestHeaders(request, wide, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
- giterr_set(GITERR_OS, "Failed to add a header to the request");
- goto on_error;
- }
-
- error = 0;
-
-on_error:
- /* We were dealing with plaintext passwords, so clean up after ourselves a bit. */
- if (wide)
- memset(wide, 0x0, wide_len * sizeof(wchar_t));
-
- if (buf.size)
- memset(buf.ptr, 0x0, buf.size);
-
- if (raw.size)
- memset(raw.ptr, 0x0, raw.size);
-
- git__free(wide);
- git_buf_free(&buf);
- git_buf_free(&raw);
- return error;
-}
-
-static int apply_default_credentials(HINTERNET request)
-{
- /* Either the caller explicitly requested that default credentials be passed,
- * or our fallback credential callback was invoked and checked that the target
- * URI was in the appropriate Internet Explorer security zone. By setting this
- * flag, we guarantee that the credentials are delivered by WinHTTP. The default
- * is "medium" which applies to the intranet and sounds like it would correspond
- * to Internet Explorer security zones, but in fact does not. */
- DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
-
- if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD)))
- return -1;
-
- return 0;
-}
-
-static int fallback_cred_acquire_cb(
- git_cred **cred,
- const char *url,
- const char *username_from_url,
- unsigned int allowed_types,
- void *payload)
-{
- int error = 1;
-
- GIT_UNUSED(username_from_url);
- GIT_UNUSED(payload);
-
- /* If the target URI supports integrated Windows authentication
- * as an authentication mechanism */
- if (GIT_CREDTYPE_DEFAULT & allowed_types) {
- wchar_t *wide_url;
-
- /* Convert URL to wide characters */
- if (git__utf8_to_16_alloc(&wide_url, url) < 0) {
- giterr_set(GITERR_OS, "Failed to convert string to wide form");
- return -1;
- }
-
- if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) {
- IInternetSecurityManager* pISM;
-
- /* And if the target URI is in the My Computer, Intranet, or Trusted zones */
- if (SUCCEEDED(CoCreateInstance(&CLSID_InternetSecurityManager, NULL,
- CLSCTX_ALL, &IID_IInternetSecurityManager, (void **)&pISM))) {
- DWORD dwZone;
-
- if (SUCCEEDED(pISM->lpVtbl->MapUrlToZone(pISM, wide_url, &dwZone, 0)) &&
- (URLZONE_LOCAL_MACHINE == dwZone ||
- URLZONE_INTRANET == dwZone ||
- URLZONE_TRUSTED == dwZone)) {
- git_cred *existing = *cred;
-
- if (existing)
- existing->free(existing);
-
- /* Then use default Windows credentials to authenticate this request */
- error = git_cred_default_new(cred);
- }
-
- pISM->lpVtbl->Release(pISM);
- }
-
- CoUninitialize();
- }
-
- git__free(wide_url);
- }
-
- return error;
-}
-
-static int certificate_check(winhttp_stream *s, int valid)
-{
- int error;
- winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
- PCERT_CONTEXT cert_ctx;
- DWORD cert_ctx_size = sizeof(cert_ctx);
- git_cert_x509 cert;
-
- /* If there is no override, we should fail if WinHTTP doesn't think it's fine */
- if (t->owner->certificate_check_cb == NULL && !valid)
- return GIT_ECERTIFICATE;
-
- if (t->owner->certificate_check_cb == NULL || !t->connection_data.use_ssl)
- return 0;
-
- if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) {
- giterr_set(GITERR_OS, "failed to get server certificate");
- return -1;
- }
-
- giterr_clear();
- cert.parent.cert_type = GIT_CERT_X509;
- cert.data = cert_ctx->pbCertEncoded;
- cert.len = cert_ctx->cbCertEncoded;
- error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->cred_acquire_payload);
- CertFreeCertificateContext(cert_ctx);
-
- if (error < 0 && !giterr_last())
- giterr_set(GITERR_NET, "user cancelled certificate check");
-
- return error;
-}
-
-static void winhttp_stream_close(winhttp_stream *s)
-{
- if (s->chunk_buffer) {
- git__free(s->chunk_buffer);
- s->chunk_buffer = NULL;
- }
-
- if (s->post_body) {
- CloseHandle(s->post_body);
- s->post_body = NULL;
- }
-
- if (s->request_uri) {
- git__free(s->request_uri);
- s->request_uri = NULL;
- }
-
- if (s->request) {
- WinHttpCloseHandle(s->request);
- s->request = NULL;
- }
-
- s->sent_request = 0;
-}
-
-/**
- * Extract the url and password from a URL. The outputs are pointers
- * into the input.
- */
-static int userpass_from_url(wchar_t **user, int *user_len,
- wchar_t **pass, int *pass_len,
- const wchar_t *url, int url_len)
-{
- URL_COMPONENTS components = { 0 };
-
- components.dwStructSize = sizeof(components);
- /* These tell WinHttpCrackUrl that we're interested in the fields */
- components.dwUserNameLength = 1;
- components.dwPasswordLength = 1;
-
- if (!WinHttpCrackUrl(url, url_len, 0, &components)) {
- giterr_set(GITERR_OS, "failed to extract user/pass from url");
- return -1;
- }
-
- *user = components.lpszUserName;
- *user_len = components.dwUserNameLength;
- *pass = components.lpszPassword;
- *pass_len = components.dwPasswordLength;
-
- return 0;
-}
-
-#define SCHEME_HTTP "http://"
-#define SCHEME_HTTPS "https://"
-
-static int winhttp_stream_connect(winhttp_stream *s)
-{
- winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
- git_buf buf = GIT_BUF_INIT;
- char *proxy_url = NULL;
- wchar_t ct[MAX_CONTENT_TYPE_LEN];
- LPCWSTR types[] = { L"*/*", NULL };
- BOOL peerdist = FALSE;
- int error = -1;
- unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
- int default_timeout = TIMEOUT_INFINITE;
- int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
- size_t i;
- const git_proxy_options *proxy_opts;
-
- /* Prepare URL */
- git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url);
-
- if (git_buf_oom(&buf))
- return -1;
-
- /* Convert URL to wide characters */
- if (git__utf8_to_16_alloc(&s->request_uri, git_buf_cstr(&buf)) < 0) {
- giterr_set(GITERR_OS, "Failed to convert string to wide form");
- goto on_error;
- }
-
- /* Establish request */
- s->request = WinHttpOpenRequest(
- t->connection,
- s->verb,
- s->request_uri,
- NULL,
- WINHTTP_NO_REFERER,
- types,
- t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0);
-
- if (!s->request) {
- giterr_set(GITERR_OS, "Failed to open request");
- goto on_error;
- }
-
- if (!WinHttpSetTimeouts(s->request, default_timeout, default_connect_timeout, default_timeout, default_timeout)) {
- giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP");
- goto on_error;
- }
-
- proxy_opts = &t->owner->proxy;
- if (proxy_opts->type == GIT_PROXY_AUTO) {
- /* Set proxy if necessary */
- if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
- goto on_error;
- }
- else if (proxy_opts->type == GIT_PROXY_SPECIFIED) {
- proxy_url = git__strdup(proxy_opts->url);
- GITERR_CHECK_ALLOC(proxy_url);
- }
-
- if (proxy_url) {
- git_buf processed_url = GIT_BUF_INIT;
- WINHTTP_PROXY_INFO proxy_info;
- wchar_t *proxy_wide;
-
- if (!git__prefixcmp(proxy_url, SCHEME_HTTP)) {
- t->proxy_connection_data.use_ssl = false;
- } else if (!git__prefixcmp(proxy_url, SCHEME_HTTPS)) {
- t->proxy_connection_data.use_ssl = true;
- } else {
- giterr_set(GITERR_NET, "invalid URL: '%s'", proxy_url);
- return -1;
- }
-
- gitno_connection_data_free_ptrs(&t->proxy_connection_data);
-
- if ((error = gitno_extract_url_parts(&t->proxy_connection_data.host, &t->proxy_connection_data.port, NULL,
- &t->proxy_connection_data.user, &t->proxy_connection_data.pass, proxy_url, NULL)) < 0)
- goto on_error;
-
- if (t->proxy_connection_data.user && t->proxy_connection_data.pass) {
- if (t->proxy_cred) {
- t->proxy_cred->free(t->proxy_cred);
- }
-
- if ((error = git_cred_userpass_plaintext_new(&t->proxy_cred, t->proxy_connection_data.user, t->proxy_connection_data.pass)) < 0)
- goto on_error;
- }
-
- if (t->proxy_connection_data.use_ssl)
- git_buf_PUTS(&processed_url, SCHEME_HTTPS);
- else
- git_buf_PUTS(&processed_url, SCHEME_HTTP);
-
- git_buf_puts(&processed_url, t->proxy_connection_data.host);
- if (t->proxy_connection_data.port)
- git_buf_printf(&processed_url, ":%s", t->proxy_connection_data.port);
-
- if (git_buf_oom(&processed_url)) {
- giterr_set_oom();
- error = -1;
- goto on_error;
- }
-
- /* Convert URL to wide characters */
- error = git__utf8_to_16_alloc(&proxy_wide, processed_url.ptr);
- git_buf_free(&processed_url);
- if (error < 0)
- goto on_error;
-
- proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
- proxy_info.lpszProxy = proxy_wide;
- proxy_info.lpszProxyBypass = NULL;
-
- if (!WinHttpSetOption(s->request,
- WINHTTP_OPTION_PROXY,
- &proxy_info,
- sizeof(WINHTTP_PROXY_INFO))) {
- giterr_set(GITERR_OS, "Failed to set proxy");
- git__free(proxy_wide);
- goto on_error;
- }
-
- git__free(proxy_wide);
-
- if (t->proxy_cred) {
- if (t->proxy_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT) {
- if ((error = apply_basic_credential_proxy(s->request, t->proxy_cred)) < 0)
- goto on_error;
- }
- }
-
- }
-
- /* Disable WinHTTP redirects so we can handle them manually. Why, you ask?
- * http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/b2ff8879-ab9f-4218-8f09-16d25dff87ae
- */
- if (!WinHttpSetOption(s->request,
- WINHTTP_OPTION_DISABLE_FEATURE,
- &disable_redirects,
- sizeof(disable_redirects))) {
- giterr_set(GITERR_OS, "Failed to disable redirects");
- goto on_error;
- }
-
- /* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
- * adds itself. This option may not be supported by the underlying
- * platform, so we do not error-check it */
- WinHttpSetOption(s->request,
- WINHTTP_OPTION_PEERDIST_EXTENSION_STATE,
- &peerdist,
- sizeof(peerdist));
-
- /* Send Pragma: no-cache header */
- if (!WinHttpAddRequestHeaders(s->request, pragma_nocache, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
- giterr_set(GITERR_OS, "Failed to add a header to the request");
- goto on_error;
- }
-
- if (post_verb == s->verb) {
- /* Send Content-Type and Accept headers -- only necessary on a POST */
- git_buf_clear(&buf);
- if (git_buf_printf(&buf,
- "Content-Type: application/x-git-%s-request",
- s->service) < 0)
- goto on_error;
-
- if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) {
- giterr_set(GITERR_OS, "Failed to convert content-type to wide characters");
- goto on_error;
- }
-
- if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
- WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
- giterr_set(GITERR_OS, "Failed to add a header to the request");
- goto on_error;
- }
-
- git_buf_clear(&buf);
- if (git_buf_printf(&buf,
- "Accept: application/x-git-%s-result",
- s->service) < 0)
- goto on_error;
-
- if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) {
- giterr_set(GITERR_OS, "Failed to convert accept header to wide characters");
- goto on_error;
- }
-
- if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
- WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
- giterr_set(GITERR_OS, "Failed to add a header to the request");
- goto on_error;
- }
- }
-
- for (i = 0; i < t->owner->custom_headers.count; i++) {
- if (t->owner->custom_headers.strings[i]) {
- git_buf_clear(&buf);
- git_buf_puts(&buf, t->owner->custom_headers.strings[i]);
- if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) {
- giterr_set(GITERR_OS, "Failed to convert custom header to wide characters");
- goto on_error;
- }
-
- if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
- WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
- giterr_set(GITERR_OS, "Failed to add a header to the request");
- goto on_error;
- }
- }
- }
-
- /* If requested, disable certificate validation */
- if (t->connection_data.use_ssl) {
- int flags;
-
- if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
- goto on_error;
- }
-
- /* If we have a credential on the subtransport, apply it to the request */
- if (t->cred &&
- t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
- t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC &&
- apply_basic_credential(s->request, t->cred) < 0)
- goto on_error;
- else if (t->cred &&
- t->cred->credtype == GIT_CREDTYPE_DEFAULT &&
- t->auth_mechanism == GIT_WINHTTP_AUTH_NEGOTIATE &&
- apply_default_credentials(s->request) < 0)
- goto on_error;
-
- /* If no other credentials have been applied and the URL has username and
- * password, use those */
- if (!t->cred && t->connection_data.user && t->connection_data.pass) {
- if (!t->url_cred &&
- git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0)
- goto on_error;
- if (apply_basic_credential(s->request, t->url_cred) < 0)
- goto on_error;
- }
-
- /* We've done everything up to calling WinHttpSendRequest. */
-
- error = 0;
-
-on_error:
- if (error < 0)
- winhttp_stream_close(s);
-
- git__free(proxy_url);
- git_buf_free(&buf);
- return error;
-}
-
-static int parse_unauthorized_response(
- HINTERNET request,
- int *allowed_types,
- int *auth_mechanism)
-{
- DWORD supported, first, target;
-
- *allowed_types = 0;
- *auth_mechanism = 0;
-
- /* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes().
- * We can assume this was already done, since we know we are unauthorized.
- */
- if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) {
- giterr_set(GITERR_OS, "Failed to parse supported auth schemes");
- return -1;
- }
-
- if (WINHTTP_AUTH_SCHEME_BASIC & supported) {
- *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
- *auth_mechanism = GIT_WINHTTP_AUTH_BASIC;
- }
-
- if ((WINHTTP_AUTH_SCHEME_NTLM & supported) ||
- (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported)) {
- *allowed_types |= GIT_CREDTYPE_DEFAULT;
- *auth_mechanism = GIT_WINHTTP_AUTH_NEGOTIATE;
- }
-
- return 0;
-}
-
-static int write_chunk(HINTERNET request, const char *buffer, size_t len)
-{
- DWORD bytes_written;
- git_buf buf = GIT_BUF_INIT;
-
- /* Chunk header */
- git_buf_printf(&buf, "%X\r\n", len);
-
- if (git_buf_oom(&buf))
- return -1;
-
- if (!WinHttpWriteData(request,
- git_buf_cstr(&buf), (DWORD)git_buf_len(&buf),
- &bytes_written)) {
- git_buf_free(&buf);
- giterr_set(GITERR_OS, "Failed to write chunk header");
- return -1;
- }
-
- git_buf_free(&buf);
-
- /* Chunk body */
- if (!WinHttpWriteData(request,
- buffer, (DWORD)len,
- &bytes_written)) {
- giterr_set(GITERR_OS, "Failed to write chunk");
- return -1;
- }
-
- /* Chunk footer */
- if (!WinHttpWriteData(request,
- "\r\n", 2,
- &bytes_written)) {
- giterr_set(GITERR_OS, "Failed to write chunk footer");
- return -1;
- }
-
- return 0;
-}
-
-static int winhttp_close_connection(winhttp_subtransport *t)
-{
- int ret = 0;
-
- if (t->connection) {
- if (!WinHttpCloseHandle(t->connection)) {
- giterr_set(GITERR_OS, "Unable to close connection");
- ret = -1;
- }
-
- t->connection = NULL;
- }
-
- if (t->session) {
- if (!WinHttpCloseHandle(t->session)) {
- giterr_set(GITERR_OS, "Unable to close session");
- ret = -1;
- }
-
- t->session = NULL;
- }
-
- return ret;
-}
-
-static int user_agent(git_buf *ua)
-{
- const char *custom = git_libgit2__user_agent();
-
- git_buf_clear(ua);
- git_buf_PUTS(ua, "git/1.0 (");
-
- if (custom)
- git_buf_puts(ua, custom);
- else
- git_buf_PUTS(ua, "libgit2 " LIBGIT2_VERSION);
-
- return git_buf_putc(ua, ')');
-}
-
-static int winhttp_connect(
- winhttp_subtransport *t)
-{
- wchar_t *wide_host;
- int32_t port;
- wchar_t *wide_ua;
- git_buf ua = GIT_BUF_INIT;
- int error = -1;
- int default_timeout = TIMEOUT_INFINITE;
- int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
-
- t->session = NULL;
- t->connection = NULL;
-
- /* Prepare port */
- if (git__strtol32(&port, t->connection_data.port, NULL, 10) < 0)
- return -1;
-
- /* Prepare host */
- if (git__utf8_to_16_alloc(&wide_host, t->connection_data.host) < 0) {
- giterr_set(GITERR_OS, "Unable to convert host to wide characters");
- return -1;
- }
-
- if ((error = user_agent(&ua)) < 0) {
- git__free(wide_host);
- return error;
- }
-
- if (git__utf8_to_16_alloc(&wide_ua, git_buf_cstr(&ua)) < 0) {
- giterr_set(GITERR_OS, "Unable to convert host to wide characters");
- git__free(wide_host);
- git_buf_free(&ua);
- return -1;
- }
-
- git_buf_free(&ua);
-
- /* Establish session */
- t->session = WinHttpOpen(
- wide_ua,
- WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
- WINHTTP_NO_PROXY_NAME,
- WINHTTP_NO_PROXY_BYPASS,
- 0);
-
- if (!t->session) {
- giterr_set(GITERR_OS, "Failed to init WinHTTP");
- goto on_error;
- }
-
- if (!WinHttpSetTimeouts(t->session, default_timeout, default_connect_timeout, default_timeout, default_timeout)) {
- giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP");
- goto on_error;
- }
-
-
- /* Establish connection */
- t->connection = WinHttpConnect(
- t->session,
- wide_host,
- (INTERNET_PORT) port,
- 0);
-
- if (!t->connection) {
- giterr_set(GITERR_OS, "Failed to connect to host");
- goto on_error;
- }
-
- error = 0;
-
-on_error:
- if (error < 0)
- winhttp_close_connection(t);
-
- git__free(wide_host);
- git__free(wide_ua);
-
- return error;
-}
-
-static int do_send_request(winhttp_stream *s, size_t len, int ignore_length)
-{
- if (ignore_length) {
- if (!WinHttpSendRequest(s->request,
- WINHTTP_NO_ADDITIONAL_HEADERS, 0,
- WINHTTP_NO_REQUEST_DATA, 0,
- WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0)) {
- return -1;
- }
- } else {
- if (!WinHttpSendRequest(s->request,
- WINHTTP_NO_ADDITIONAL_HEADERS, 0,
- WINHTTP_NO_REQUEST_DATA, 0,
- len, 0)) {
- return -1;
- }
- }
-
- return 0;
-}
-
-static int send_request(winhttp_stream *s, size_t len, int ignore_length)
-{
- int request_failed = 0, cert_valid = 1, error = 0;
- DWORD ignore_flags;
-
- if ((error = do_send_request(s, len, ignore_length)) < 0)
- request_failed = 1;
-
- if (request_failed) {
- if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) {
- giterr_set(GITERR_OS, "failed to send request");
- return -1;
- } else {
- cert_valid = 0;
- }
- }
-
- giterr_clear();
- if ((error = certificate_check(s, cert_valid)) < 0) {
- if (!giterr_last())
- giterr_set(GITERR_OS, "user cancelled certificate check");
-
- return error;
- }
-
- /* if neither the request nor the certificate check returned errors, we're done */
- if (!request_failed)
- return 0;
-
- ignore_flags = no_check_cert_flags;
-
- if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) {
- giterr_set(GITERR_OS, "failed to set security options");
- return -1;
- }
-
- if ((error = do_send_request(s, len, ignore_length)) < 0)
- giterr_set(GITERR_OS, "failed to send request");
-
- return error;
-}
-
-static int winhttp_stream_read(
- git_smart_subtransport_stream *stream,
- char *buffer,
- size_t buf_size,
- size_t *bytes_read)
-{
- winhttp_stream *s = (winhttp_stream *)stream;
- winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
- DWORD dw_bytes_read;
- char replay_count = 0;
- int error;
-
-replay:
- /* Enforce a reasonable cap on the number of replays */
- if (++replay_count >= 7) {
- giterr_set(GITERR_NET, "Too many redirects or authentication replays");
- return -1;
- }
-
- /* Connect if necessary */
- if (!s->request && winhttp_stream_connect(s) < 0)
- return -1;
-
- if (!s->received_response) {
- DWORD status_code, status_code_length, content_type_length, bytes_written;
- char expected_content_type_8[MAX_CONTENT_TYPE_LEN];
- wchar_t expected_content_type[MAX_CONTENT_TYPE_LEN], content_type[MAX_CONTENT_TYPE_LEN];
-
- if (!s->sent_request) {
-
- if ((error = send_request(s, s->post_body_len, 0)) < 0)
- return error;
-
- s->sent_request = 1;
- }
-
- if (s->chunked) {
- assert(s->verb == post_verb);
-
- /* Flush, if necessary */
- if (s->chunk_buffer_len > 0 &&
- write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
- return -1;
-
- s->chunk_buffer_len = 0;
-
- /* Write the final chunk. */
- if (!WinHttpWriteData(s->request,
- "0\r\n\r\n", 5,
- &bytes_written)) {
- giterr_set(GITERR_OS, "Failed to write final chunk");
- return -1;
- }
- }
- else if (s->post_body) {
- char *buffer;
- DWORD len = s->post_body_len, bytes_read;
-
- if (INVALID_SET_FILE_POINTER == SetFilePointer(s->post_body,
- 0, 0, FILE_BEGIN) &&
- NO_ERROR != GetLastError()) {
- giterr_set(GITERR_OS, "Failed to reset file pointer");
- return -1;
- }
-
- buffer = git__malloc(CACHED_POST_BODY_BUF_SIZE);
-
- while (len > 0) {
- DWORD bytes_written;
-
- if (!ReadFile(s->post_body, buffer,
- min(CACHED_POST_BODY_BUF_SIZE, len),
- &bytes_read, NULL) ||
- !bytes_read) {
- git__free(buffer);
- giterr_set(GITERR_OS, "Failed to read from temp file");
- return -1;
- }
-
- if (!WinHttpWriteData(s->request, buffer,
- bytes_read, &bytes_written)) {
- git__free(buffer);
- giterr_set(GITERR_OS, "Failed to write data");
- return -1;
- }
-
- len -= bytes_read;
- assert(bytes_read == bytes_written);
- }
-
- git__free(buffer);
-
- /* Eagerly close the temp file */
- CloseHandle(s->post_body);
- s->post_body = NULL;
- }
-
- if (!WinHttpReceiveResponse(s->request, 0)) {
- giterr_set(GITERR_OS, "Failed to receive response");
- return -1;
- }
-
- /* Verify that we got a 200 back */
- status_code_length = sizeof(status_code);
-
- if (!WinHttpQueryHeaders(s->request,
- WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
- WINHTTP_HEADER_NAME_BY_INDEX,
- &status_code, &status_code_length,
- WINHTTP_NO_HEADER_INDEX)) {
- giterr_set(GITERR_OS, "Failed to retrieve status code");
- return -1;
- }
-
- /* The implementation of WinHTTP prior to Windows 7 will not
- * redirect to an identical URI. Some Git hosters use self-redirects
- * as part of their DoS mitigation strategy. Check first to see if we
- * have a redirect status code, and that we haven't already streamed
- * a post body. (We can't replay a streamed POST.) */
- if (!s->chunked &&
- (HTTP_STATUS_MOVED == status_code ||
- HTTP_STATUS_REDIRECT == status_code ||
- (HTTP_STATUS_REDIRECT_METHOD == status_code &&
- get_verb == s->verb) ||
- HTTP_STATUS_REDIRECT_KEEP_VERB == status_code)) {
-
- /* Check for Windows 7. This workaround is only necessary on
- * Windows Vista and earlier. Windows 7 is version 6.1. */
- wchar_t *location;
- DWORD location_length;
- char *location8;
-
- /* OK, fetch the Location header from the redirect. */
- if (WinHttpQueryHeaders(s->request,
- WINHTTP_QUERY_LOCATION,
- WINHTTP_HEADER_NAME_BY_INDEX,
- WINHTTP_NO_OUTPUT_BUFFER,
- &location_length,
- WINHTTP_NO_HEADER_INDEX) ||
- GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
- giterr_set(GITERR_OS, "Failed to read Location header");
- return -1;
- }
-
- location = git__malloc(location_length);
- GITERR_CHECK_ALLOC(location);
-
- if (!WinHttpQueryHeaders(s->request,
- WINHTTP_QUERY_LOCATION,
- WINHTTP_HEADER_NAME_BY_INDEX,
- location,
- &location_length,
- WINHTTP_NO_HEADER_INDEX)) {
- giterr_set(GITERR_OS, "Failed to read Location header");
- git__free(location);
- return -1;
- }
-
- /* Convert the Location header to UTF-8 */
- if (git__utf16_to_8_alloc(&location8, location) < 0) {
- giterr_set(GITERR_OS, "Failed to convert Location header to UTF-8");
- git__free(location);
- return -1;
- }
-
- git__free(location);
-
- /* Replay the request */
- winhttp_stream_close(s);
-
- if (!git__prefixcmp_icase(location8, prefix_https)) {
- /* Upgrade to secure connection; disconnect and start over */
- if (gitno_connection_data_from_url(&t->connection_data, location8, s->service_url) < 0) {
- git__free(location8);
- return -1;
- }
-
- winhttp_close_connection(t);
-
- if (winhttp_connect(t) < 0)
- return -1;
- }
-
- git__free(location8);
- goto replay;
- }
-
- /* Handle proxy authentication failures */
- if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) {
- int allowed_types;
-
- if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
- return -1;
-
- /* TODO: extract the username from the url, no payload? */
- if (t->owner->proxy.credentials) {
- int cred_error = 1;
- cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, NULL);
-
- if (cred_error < 0)
- return cred_error;
- }
-
- winhttp_stream_close(s);
- goto replay;
- }
-
- /* Handle authentication failures */
- if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) {
- int allowed_types;
-
- if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
- return -1;
-
- if (allowed_types) {
- int cred_error = 1;
-
- git_cred_free(t->cred);
- t->cred = NULL;
- /* Start with the user-supplied credential callback, if present */
- if (t->owner->cred_acquire_cb) {
- cred_error = t->owner->cred_acquire_cb(&t->cred, t->owner->url,
- t->connection_data.user, allowed_types, t->owner->cred_acquire_payload);
-
- /* Treat GIT_PASSTHROUGH as though git_cred_acquire_cb isn't set */
- if (cred_error == GIT_PASSTHROUGH)
- cred_error = 1;
- else if (cred_error < 0)
- return cred_error;
- }
-
- /* Invoke the fallback credentials acquisition callback if necessary */
- if (cred_error > 0) {
- cred_error = fallback_cred_acquire_cb(&t->cred, t->owner->url,
- t->connection_data.user, allowed_types, NULL);
-
- if (cred_error < 0)
- return cred_error;
- }
-
- if (!cred_error) {
- assert(t->cred);
-
- winhttp_stream_close(s);
-
- /* Successfully acquired a credential */
- goto replay;
- }
- }
- }
-
- if (HTTP_STATUS_OK != status_code) {
- giterr_set(GITERR_NET, "Request failed with status code: %d", status_code);
- return -1;
- }
-
- /* Verify that we got the correct content-type back */
- if (post_verb == s->verb)
- p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service);
- else
- p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
-
- if (git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) {
- giterr_set(GITERR_OS, "Failed to convert expected content-type to wide characters");
- return -1;
- }
-
- content_type_length = sizeof(content_type);
-
- if (!WinHttpQueryHeaders(s->request,
- WINHTTP_QUERY_CONTENT_TYPE,
- WINHTTP_HEADER_NAME_BY_INDEX,
- &content_type, &content_type_length,
- WINHTTP_NO_HEADER_INDEX)) {
- giterr_set(GITERR_OS, "Failed to retrieve response content-type");
- return -1;
- }
-
- if (wcscmp(expected_content_type, content_type)) {
- giterr_set(GITERR_NET, "Received unexpected content-type");
- return -1;
- }
-
- s->received_response = 1;
- }
-
- if (!WinHttpReadData(s->request,
- (LPVOID)buffer,
- (DWORD)buf_size,
- &dw_bytes_read))
- {
- giterr_set(GITERR_OS, "Failed to read data");
- return -1;
- }
-
- *bytes_read = dw_bytes_read;
-
- return 0;
-}
-
-static int winhttp_stream_write_single(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len)
-{
- winhttp_stream *s = (winhttp_stream *)stream;
- DWORD bytes_written;
- int error;
-
- if (!s->request && winhttp_stream_connect(s) < 0)
- return -1;
-
- /* This implementation of write permits only a single call. */
- if (s->sent_request) {
- giterr_set(GITERR_NET, "Subtransport configured for only one write");
- return -1;
- }
-
- if ((error = send_request(s, len, 0)) < 0)
- return error;
-
- s->sent_request = 1;
-
- if (!WinHttpWriteData(s->request,
- (LPCVOID)buffer,
- (DWORD)len,
- &bytes_written)) {
- giterr_set(GITERR_OS, "Failed to write data");
- return -1;
- }
-
- assert((DWORD)len == bytes_written);
-
- return 0;
-}
-
-static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch)
-{
- UUID uuid;
- RPC_STATUS status = UuidCreate(&uuid);
- int result;
-
- if (RPC_S_OK != status &&
- RPC_S_UUID_LOCAL_ONLY != status &&
- RPC_S_UUID_NO_ADDRESS != status) {
- giterr_set(GITERR_NET, "Unable to generate name for temp file");
- return -1;
- }
-
- if (buffer_len_cch < UUID_LENGTH_CCH + 1) {
- giterr_set(GITERR_NET, "Buffer too small for name of temp file");
- return -1;
- }
-
-#if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
- result = swprintf_s(buffer, buffer_len_cch,
-#else
- result = wsprintfW(buffer,
-#endif
- L"%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x",
- uuid.Data1, uuid.Data2, uuid.Data3,
- uuid.Data4[0], uuid.Data4[1], uuid.Data4[2], uuid.Data4[3],
- uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7]);
-
- if (result < UUID_LENGTH_CCH) {
- giterr_set(GITERR_OS, "Unable to generate name for temp file");
- return -1;
- }
-
- return 0;
-}
-
-static int get_temp_file(LPWSTR buffer, DWORD buffer_len_cch)
-{
- size_t len;
-
- if (!GetTempPathW(buffer_len_cch, buffer)) {
- giterr_set(GITERR_OS, "Failed to get temp path");
- return -1;
- }
-
- len = wcslen(buffer);
-
- if (buffer[len - 1] != '\\' && len < buffer_len_cch)
- buffer[len++] = '\\';
-
- if (put_uuid_string(&buffer[len], (size_t)buffer_len_cch - len) < 0)
- return -1;
-
- return 0;
-}
-
-static int winhttp_stream_write_buffered(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len)
-{
- winhttp_stream *s = (winhttp_stream *)stream;
- DWORD bytes_written;
-
- if (!s->request && winhttp_stream_connect(s) < 0)
- return -1;
-
- /* Buffer the payload, using a temporary file so we delegate
- * memory management of the data to the operating system. */
- if (!s->post_body) {
- wchar_t temp_path[MAX_PATH + 1];
-
- if (get_temp_file(temp_path, MAX_PATH + 1) < 0)
- return -1;
-
- s->post_body = CreateFileW(temp_path,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_DELETE, NULL,
- CREATE_NEW,
- FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_SEQUENTIAL_SCAN,
- NULL);
-
- if (INVALID_HANDLE_VALUE == s->post_body) {
- s->post_body = NULL;
- giterr_set(GITERR_OS, "Failed to create temporary file");
- return -1;
- }
- }
-
- if (!WriteFile(s->post_body, buffer, (DWORD)len, &bytes_written, NULL)) {
- giterr_set(GITERR_OS, "Failed to write to temporary file");
- return -1;
- }
-
- assert((DWORD)len == bytes_written);
-
- s->post_body_len += bytes_written;
-
- return 0;
-}
-
-static int winhttp_stream_write_chunked(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len)
-{
- winhttp_stream *s = (winhttp_stream *)stream;
- int error;
-
- if (!s->request && winhttp_stream_connect(s) < 0)
- return -1;
-
- if (!s->sent_request) {
- /* Send Transfer-Encoding: chunked header */
- if (!WinHttpAddRequestHeaders(s->request,
- transfer_encoding, (ULONG) -1L,
- WINHTTP_ADDREQ_FLAG_ADD)) {
- giterr_set(GITERR_OS, "Failed to add a header to the request");
- return -1;
- }
-
- if ((error = send_request(s, 0, 1)) < 0)
- return error;
-
- s->sent_request = 1;
- }
-
- if (len > CACHED_POST_BODY_BUF_SIZE) {
- /* Flush, if necessary */
- if (s->chunk_buffer_len > 0) {
- if (write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
- return -1;
-
- s->chunk_buffer_len = 0;
- }
-
- /* Write chunk directly */
- if (write_chunk(s->request, buffer, len) < 0)
- return -1;
- }
- else {
- /* Append as much to the buffer as we can */
- int count = (int)min(CACHED_POST_BODY_BUF_SIZE - s->chunk_buffer_len, len);
-
- if (!s->chunk_buffer)
- s->chunk_buffer = git__malloc(CACHED_POST_BODY_BUF_SIZE);
-
- memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
- s->chunk_buffer_len += count;
- buffer += count;
- len -= count;
-
- /* Is the buffer full? If so, then flush */
- if (CACHED_POST_BODY_BUF_SIZE == s->chunk_buffer_len) {
- if (write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
- return -1;
-
- s->chunk_buffer_len = 0;
-
- /* Is there any remaining data from the source? */
- if (len > 0) {
- memcpy(s->chunk_buffer, buffer, len);
- s->chunk_buffer_len = (unsigned int)len;
- }
- }
- }
-
- return 0;
-}
-
-static void winhttp_stream_free(git_smart_subtransport_stream *stream)
-{
- winhttp_stream *s = (winhttp_stream *)stream;
-
- winhttp_stream_close(s);
- git__free(s);
-}
-
-static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream)
-{
- winhttp_stream *s;
-
- if (!stream)
- return -1;
-
- s = git__calloc(1, sizeof(winhttp_stream));
- GITERR_CHECK_ALLOC(s);
-
- s->parent.subtransport = &t->parent;
- s->parent.read = winhttp_stream_read;
- s->parent.write = winhttp_stream_write_single;
- s->parent.free = winhttp_stream_free;
-
- *stream = s;
-
- return 0;
-}
-
-static int winhttp_uploadpack_ls(
- winhttp_subtransport *t,
- winhttp_stream *s)
-{
- GIT_UNUSED(t);
-
- s->service = upload_pack_service;
- s->service_url = upload_pack_ls_service_url;
- s->verb = get_verb;
-
- return 0;
-}
-
-static int winhttp_uploadpack(
- winhttp_subtransport *t,
- winhttp_stream *s)
-{
- GIT_UNUSED(t);
-
- s->service = upload_pack_service;
- s->service_url = upload_pack_service_url;
- s->verb = post_verb;
-
- return 0;
-}
-
-static int winhttp_receivepack_ls(
- winhttp_subtransport *t,
- winhttp_stream *s)
-{
- GIT_UNUSED(t);
-
- s->service = receive_pack_service;
- s->service_url = receive_pack_ls_service_url;
- s->verb = get_verb;
-
- return 0;
-}
-
-static int winhttp_receivepack(
- winhttp_subtransport *t,
- winhttp_stream *s)
-{
- GIT_UNUSED(t);
-
- /* WinHTTP only supports Transfer-Encoding: chunked
- * on Windows Vista (NT 6.0) and higher. */
- s->chunked = git_has_win32_version(6, 0, 0);
-
- if (s->chunked)
- s->parent.write = winhttp_stream_write_chunked;
- else
- s->parent.write = winhttp_stream_write_buffered;
-
- s->service = receive_pack_service;
- s->service_url = receive_pack_service_url;
- s->verb = post_verb;
-
- return 0;
-}
-
-static int winhttp_action(
- git_smart_subtransport_stream **stream,
- git_smart_subtransport *subtransport,
- const char *url,
- git_smart_service_t action)
-{
- winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
- winhttp_stream *s;
- int ret = -1;
-
- if (!t->connection)
- if ((ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0 ||
- (ret = winhttp_connect(t)) < 0)
- return ret;
-
- if (winhttp_stream_alloc(t, &s) < 0)
- return -1;
-
- if (!stream)
- return -1;
-
- switch (action)
- {
- case GIT_SERVICE_UPLOADPACK_LS:
- ret = winhttp_uploadpack_ls(t, s);
- break;
-
- case GIT_SERVICE_UPLOADPACK:
- ret = winhttp_uploadpack(t, s);
- break;
-
- case GIT_SERVICE_RECEIVEPACK_LS:
- ret = winhttp_receivepack_ls(t, s);
- break;
-
- case GIT_SERVICE_RECEIVEPACK:
- ret = winhttp_receivepack(t, s);
- break;
-
- default:
- assert(0);
- }
-
- if (!ret)
- *stream = &s->parent;
-
- return ret;
-}
-
-static int winhttp_close(git_smart_subtransport *subtransport)
-{
- winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
-
- gitno_connection_data_free_ptrs(&t->connection_data);
- memset(&t->connection_data, 0x0, sizeof(gitno_connection_data));
- gitno_connection_data_free_ptrs(&t->proxy_connection_data);
- memset(&t->proxy_connection_data, 0x0, sizeof(gitno_connection_data));
-
- if (t->cred) {
- t->cred->free(t->cred);
- t->cred = NULL;
- }
-
- if (t->proxy_cred) {
- t->proxy_cred->free(t->proxy_cred);
- t->proxy_cred = NULL;
- }
-
- if (t->url_cred) {
- t->url_cred->free(t->url_cred);
- t->url_cred = NULL;
- }
-
- return winhttp_close_connection(t);
-}
-
-static void winhttp_free(git_smart_subtransport *subtransport)
-{
- winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
-
- winhttp_close(subtransport);
-
- git__free(t);
-}
-
-int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
-{
- winhttp_subtransport *t;
-
- GIT_UNUSED(param);
-
- if (!out)
- return -1;
-
- t = git__calloc(1, sizeof(winhttp_subtransport));
- GITERR_CHECK_ALLOC(t);
-
- t->owner = (transport_smart *)owner;
- t->parent.action = winhttp_action;
- t->parent.close = winhttp_close;
- t->parent.free = winhttp_free;
-
- *out = (git_smart_subtransport *) t;
- return 0;
-}
-
-#endif /* GIT_WINHTTP */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "tree-cache.h"
-#include "pool.h"
-#include "tree.h"
-
-static git_tree_cache *find_child(
- const git_tree_cache *tree, const char *path, const char *end)
-{
- size_t i, dirlen = end ? (size_t)(end - path) : strlen(path);
-
- for (i = 0; i < tree->children_count; ++i) {
- git_tree_cache *child = tree->children[i];
-
- if (child->namelen == dirlen && !memcmp(path, child->name, dirlen))
- return child;
- }
-
- return NULL;
-}
-
-void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path)
-{
- const char *ptr = path, *end;
-
- if (tree == NULL)
- return;
-
- tree->entry_count = -1;
-
- while (ptr != NULL) {
- end = strchr(ptr, '/');
-
- if (end == NULL) /* End of path */
- break;
-
- tree = find_child(tree, ptr, end);
- if (tree == NULL) /* We don't have that tree */
- return;
-
- tree->entry_count = -1;
- ptr = end + 1;
- }
-}
-
-const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path)
-{
- const char *ptr = path, *end;
-
- if (tree == NULL) {
- return NULL;
- }
-
- while (1) {
- end = strchr(ptr, '/');
-
- tree = find_child(tree, ptr, end);
- if (tree == NULL) /* Can't find it */
- return NULL;
-
- if (end == NULL || *end + 1 == '\0')
- return tree;
-
- ptr = end + 1;
- }
-}
-
-static int read_tree_internal(git_tree_cache **out,
- const char **buffer_in, const char *buffer_end,
- git_pool *pool)
-{
- git_tree_cache *tree = NULL;
- const char *name_start, *buffer;
- int count;
-
- buffer = name_start = *buffer_in;
-
- if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)
- goto corrupted;
-
- if (++buffer >= buffer_end)
- goto corrupted;
-
- if (git_tree_cache_new(&tree, name_start, pool) < 0)
- return -1;
-
- /* Blank-terminated ASCII decimal number of entries in this tree */
- if (git__strtol32(&count, buffer, &buffer, 10) < 0)
- goto corrupted;
-
- tree->entry_count = count;
-
- if (*buffer != ' ' || ++buffer >= buffer_end)
- goto corrupted;
-
- /* Number of children of the tree, newline-terminated */
- if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < 0)
- goto corrupted;
-
- tree->children_count = count;
-
- if (*buffer != '\n' || ++buffer > buffer_end)
- goto corrupted;
-
- /* The SHA1 is only there if it's not invalidated */
- if (tree->entry_count >= 0) {
- /* 160-bit SHA-1 for this tree and it's children */
- if (buffer + GIT_OID_RAWSZ > buffer_end)
- goto corrupted;
-
- git_oid_fromraw(&tree->oid, (const unsigned char *)buffer);
- buffer += GIT_OID_RAWSZ;
- }
-
- /* Parse children: */
- if (tree->children_count > 0) {
- unsigned int i;
-
- tree->children = git_pool_malloc(pool, tree->children_count * sizeof(git_tree_cache *));
- GITERR_CHECK_ALLOC(tree->children);
-
- memset(tree->children, 0x0, tree->children_count * sizeof(git_tree_cache *));
-
- for (i = 0; i < tree->children_count; ++i) {
- if (read_tree_internal(&tree->children[i], &buffer, buffer_end, pool) < 0)
- goto corrupted;
- }
- }
-
- *buffer_in = buffer;
- *out = tree;
- return 0;
-
- corrupted:
- giterr_set(GITERR_INDEX, "Corrupted TREE extension in index");
- return -1;
-}
-
-int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool)
-{
- const char *buffer_end = buffer + buffer_size;
-
- if (read_tree_internal(tree, &buffer, buffer_end, pool) < 0)
- return -1;
-
- if (buffer < buffer_end) {
- giterr_set(GITERR_INDEX, "Corrupted TREE extension in index (unexpected trailing data)");
- return -1;
- }
-
- return 0;
-}
-
-static int read_tree_recursive(git_tree_cache *cache, const git_tree *tree, git_pool *pool)
-{
- git_repository *repo;
- size_t i, j, nentries, ntrees;
- int error;
-
- repo = git_tree_owner(tree);
-
- git_oid_cpy(&cache->oid, git_tree_id(tree));
- nentries = git_tree_entrycount(tree);
-
- /*
- * We make sure we know how many trees we need to allocate for
- * so we don't have to realloc and change the pointers for the
- * parents.
- */
- ntrees = 0;
- for (i = 0; i < nentries; i++) {
- const git_tree_entry *entry;
-
- entry = git_tree_entry_byindex(tree, i);
- if (git_tree_entry_filemode(entry) == GIT_FILEMODE_TREE)
- ntrees++;
- }
-
- cache->children_count = ntrees;
- cache->children = git_pool_mallocz(pool, ntrees * sizeof(git_tree_cache *));
- GITERR_CHECK_ALLOC(cache->children);
-
- j = 0;
- for (i = 0; i < nentries; i++) {
- const git_tree_entry *entry;
- git_tree *subtree;
-
- entry = git_tree_entry_byindex(tree, i);
- if (git_tree_entry_filemode(entry) != GIT_FILEMODE_TREE) {
- cache->entry_count++;
- continue;
- }
-
- if ((error = git_tree_cache_new(&cache->children[j], git_tree_entry_name(entry), pool)) < 0)
- return error;
-
- if ((error = git_tree_lookup(&subtree, repo, git_tree_entry_id(entry))) < 0)
- return error;
-
- error = read_tree_recursive(cache->children[j], subtree, pool);
- git_tree_free(subtree);
- cache->entry_count += cache->children[j]->entry_count;
- j++;
-
- if (error < 0)
- return error;
- }
-
- return 0;
-}
-
-int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool)
-{
- int error;
- git_tree_cache *cache;
-
- if ((error = git_tree_cache_new(&cache, "", pool)) < 0)
- return error;
-
- if ((error = read_tree_recursive(cache, tree, pool)) < 0)
- return error;
-
- *out = cache;
- return 0;
-}
-
-int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool)
-{
- size_t name_len;
- git_tree_cache *tree;
-
- name_len = strlen(name);
- tree = git_pool_malloc(pool, sizeof(git_tree_cache) + name_len + 1);
- GITERR_CHECK_ALLOC(tree);
-
- memset(tree, 0x0, sizeof(git_tree_cache));
- /* NUL-terminated tree name */
- tree->namelen = name_len;
- memcpy(tree->name, name, name_len);
- tree->name[name_len] = '\0';
-
- *out = tree;
- return 0;
-}
-
-static void write_tree(git_buf *out, git_tree_cache *tree)
-{
- size_t i;
-
- git_buf_printf(out, "%s%c%"PRIdZ" %"PRIuZ"\n", tree->name, 0, tree->entry_count, tree->children_count);
-
- if (tree->entry_count != -1)
- git_buf_put(out, (const char *) &tree->oid, GIT_OID_RAWSZ);
-
- for (i = 0; i < tree->children_count; i++)
- write_tree(out, tree->children[i]);
-}
-
-int git_tree_cache_write(git_buf *out, git_tree_cache *tree)
-{
- write_tree(out, tree);
-
- return git_buf_oom(out) ? -1 : 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_tree_cache_h__
-#define INCLUDE_tree_cache_h__
-
-#include "common.h"
-#include "pool.h"
-#include "buffer.h"
-#include "git2/oid.h"
-
-typedef struct git_tree_cache {
- struct git_tree_cache **children;
- size_t children_count;
-
- ssize_t entry_count;
- git_oid oid;
- size_t namelen;
- char name[GIT_FLEX_ARRAY];
-} git_tree_cache;
-
-int git_tree_cache_write(git_buf *out, git_tree_cache *tree);
-int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool);
-void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path);
-const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path);
-int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool);
-/**
- * Read a tree as the root of the tree cache (like for `git read-tree`)
- */
-int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool);
-void git_tree_cache_free(git_tree_cache *tree);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "commit.h"
-#include "tree.h"
-#include "git2/repository.h"
-#include "git2/object.h"
-#include "fileops.h"
-#include "tree-cache.h"
-#include "index.h"
-
-#define DEFAULT_TREE_SIZE 16
-#define MAX_FILEMODE_BYTES 6
-
-#define TREE_ENTRY_CHECK_NAMELEN(n) \
- if (n > UINT16_MAX) { giterr_set(GITERR_INVALID, "tree entry path too long"); }
-
-GIT__USE_STRMAP
-
-static bool valid_filemode(const int filemode)
-{
- return (filemode == GIT_FILEMODE_TREE
- || filemode == GIT_FILEMODE_BLOB
- || filemode == GIT_FILEMODE_BLOB_EXECUTABLE
- || filemode == GIT_FILEMODE_LINK
- || filemode == GIT_FILEMODE_COMMIT);
-}
-
-GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode)
-{
- /* Tree bits set, but it's not a commit */
- if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_TREE)
- return GIT_FILEMODE_TREE;
-
- /* If any of the x bits are set */
- if (GIT_PERMS_IS_EXEC(filemode))
- return GIT_FILEMODE_BLOB_EXECUTABLE;
-
- /* 16XXXX means commit */
- if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_COMMIT)
- return GIT_FILEMODE_COMMIT;
-
- /* 12XXXX means symlink */
- if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_LINK)
- return GIT_FILEMODE_LINK;
-
- /* Otherwise, return a blob */
- return GIT_FILEMODE_BLOB;
-}
-
-static int valid_entry_name(git_repository *repo, const char *filename)
-{
- return *filename != '\0' &&
- git_path_isvalid(repo, filename,
- GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT | GIT_PATH_REJECT_SLASH);
-}
-
-static int entry_sort_cmp(const void *a, const void *b)
-{
- const git_tree_entry *e1 = (const git_tree_entry *)a;
- const git_tree_entry *e2 = (const git_tree_entry *)b;
-
- return git_path_cmp(
- e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
- e2->filename, e2->filename_len, git_tree_entry__is_tree(e2),
- git__strncmp);
-}
-
-int git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2)
-{
- return entry_sort_cmp(e1, e2);
-}
-
-int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2)
-{
- return git_path_cmp(
- e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
- e2->filename, e2->filename_len, git_tree_entry__is_tree(e2),
- git__strncasecmp);
-}
-
-/**
- * Allocate a new self-contained entry, with enough space after it to
- * store the filename and the id.
- */
-static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, const git_oid *id)
-{
- git_tree_entry *entry = NULL;
- size_t tree_len;
-
- TREE_ENTRY_CHECK_NAMELEN(filename_len);
-
- if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) ||
- GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) ||
- GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, GIT_OID_RAWSZ))
- return NULL;
-
- entry = git__calloc(1, tree_len);
- if (!entry)
- return NULL;
-
- {
- char *filename_ptr;
- void *id_ptr;
-
- filename_ptr = ((char *) entry) + sizeof(git_tree_entry);
- memcpy(filename_ptr, filename, filename_len);
- entry->filename = filename_ptr;
-
- id_ptr = filename_ptr + filename_len + 1;
- git_oid_cpy(id_ptr, id);
- entry->oid = id_ptr;
- }
-
- entry->filename_len = (uint16_t)filename_len;
-
- return entry;
-}
-
-struct tree_key_search {
- const char *filename;
- uint16_t filename_len;
-};
-
-static int homing_search_cmp(const void *key, const void *array_member)
-{
- const struct tree_key_search *ksearch = key;
- const git_tree_entry *entry = array_member;
-
- const uint16_t len1 = ksearch->filename_len;
- const uint16_t len2 = entry->filename_len;
-
- return memcmp(
- ksearch->filename,
- entry->filename,
- len1 < len2 ? len1 : len2
- );
-}
-
-/*
- * Search for an entry in a given tree.
- *
- * Note that this search is performed in two steps because
- * of the way tree entries are sorted internally in git:
- *
- * Entries in a tree are not sorted alphabetically; two entries
- * with the same root prefix will have different positions
- * depending on whether they are folders (subtrees) or normal files.
- *
- * Consequently, it is not possible to find an entry on the tree
- * with a binary search if you don't know whether the filename
- * you're looking for is a folder or a normal file.
- *
- * To work around this, we first perform a homing binary search
- * on the tree, using the minimal length root prefix of our filename.
- * Once the comparisons for this homing search start becoming
- * ambiguous because of folder vs file sorting, we look linearly
- * around the area for our target file.
- */
-static int tree_key_search(
- size_t *at_pos,
- const git_tree *tree,
- const char *filename,
- size_t filename_len)
-{
- struct tree_key_search ksearch;
- const git_tree_entry *entry;
- size_t homing, i;
-
- TREE_ENTRY_CHECK_NAMELEN(filename_len);
-
- ksearch.filename = filename;
- ksearch.filename_len = (uint16_t)filename_len;
-
- /* Initial homing search; find an entry on the tree with
- * the same prefix as the filename we're looking for */
-
- if (git_array_search(&homing,
- tree->entries, &homing_search_cmp, &ksearch) < 0)
- return GIT_ENOTFOUND; /* just a signal error; not passed back to user */
-
- /* We found a common prefix. Look forward as long as
- * there are entries that share the common prefix */
- for (i = homing; i < tree->entries.size; ++i) {
- entry = git_array_get(tree->entries, i);
-
- if (homing_search_cmp(&ksearch, entry) < 0)
- break;
-
- if (entry->filename_len == filename_len &&
- memcmp(filename, entry->filename, filename_len) == 0) {
- if (at_pos)
- *at_pos = i;
-
- return 0;
- }
- }
-
- /* If we haven't found our filename yet, look backwards
- * too as long as we have entries with the same prefix */
- if (homing > 0) {
- i = homing - 1;
-
- do {
- entry = git_array_get(tree->entries, i);
-
- if (homing_search_cmp(&ksearch, entry) > 0)
- break;
-
- if (entry->filename_len == filename_len &&
- memcmp(filename, entry->filename, filename_len) == 0) {
- if (at_pos)
- *at_pos = i;
-
- return 0;
- }
- } while (i-- > 0);
- }
-
- /* The filename doesn't exist at all */
- return GIT_ENOTFOUND;
-}
-
-void git_tree_entry_free(git_tree_entry *entry)
-{
- if (entry == NULL)
- return;
-
- git__free(entry);
-}
-
-int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source)
-{
- git_tree_entry *cpy;
-
- assert(source);
-
- cpy = alloc_entry(source->filename, source->filename_len, source->oid);
- if (cpy == NULL)
- return -1;
-
- cpy->attr = source->attr;
-
- *dest = cpy;
- return 0;
-}
-
-void git_tree__free(void *_tree)
-{
- git_tree *tree = _tree;
-
- git_odb_object_free(tree->odb_obj);
- git_array_clear(tree->entries);
- git__free(tree);
-}
-
-git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry)
-{
- return normalize_filemode(entry->attr);
-}
-
-git_filemode_t git_tree_entry_filemode_raw(const git_tree_entry *entry)
-{
- return entry->attr;
-}
-
-const char *git_tree_entry_name(const git_tree_entry *entry)
-{
- assert(entry);
- return entry->filename;
-}
-
-const git_oid *git_tree_entry_id(const git_tree_entry *entry)
-{
- assert(entry);
- return entry->oid;
-}
-
-git_otype git_tree_entry_type(const git_tree_entry *entry)
-{
- assert(entry);
-
- if (S_ISGITLINK(entry->attr))
- return GIT_OBJ_COMMIT;
- else if (S_ISDIR(entry->attr))
- return GIT_OBJ_TREE;
- else
- return GIT_OBJ_BLOB;
-}
-
-int git_tree_entry_to_object(
- git_object **object_out,
- git_repository *repo,
- const git_tree_entry *entry)
-{
- assert(entry && object_out);
- return git_object_lookup(object_out, repo, entry->oid, GIT_OBJ_ANY);
-}
-
-static const git_tree_entry *entry_fromname(
- const git_tree *tree, const char *name, size_t name_len)
-{
- size_t idx;
-
- if (tree_key_search(&idx, tree, name, name_len) < 0)
- return NULL;
-
- return git_array_get(tree->entries, idx);
-}
-
-const git_tree_entry *git_tree_entry_byname(
- const git_tree *tree, const char *filename)
-{
- assert(tree && filename);
-
- return entry_fromname(tree, filename, strlen(filename));
-}
-
-const git_tree_entry *git_tree_entry_byindex(
- const git_tree *tree, size_t idx)
-{
- assert(tree);
- return git_array_get(tree->entries, idx);
-}
-
-const git_tree_entry *git_tree_entry_byid(
- const git_tree *tree, const git_oid *id)
-{
- size_t i;
- const git_tree_entry *e;
-
- assert(tree);
-
- git_array_foreach(tree->entries, i, e) {
- if (memcmp(&e->oid->id, &id->id, sizeof(id->id)) == 0)
- return e;
- }
-
- return NULL;
-}
-
-int git_tree__prefix_position(const git_tree *tree, const char *path)
-{
- struct tree_key_search ksearch;
- size_t at_pos, path_len;
-
- if (!path)
- return 0;
-
- path_len = strlen(path);
- TREE_ENTRY_CHECK_NAMELEN(path_len);
-
- ksearch.filename = path;
- ksearch.filename_len = (uint16_t)path_len;
-
- /* Find tree entry with appropriate prefix */
- git_array_search(
- &at_pos, tree->entries, &homing_search_cmp, &ksearch);
-
- for (; at_pos < tree->entries.size; ++at_pos) {
- const git_tree_entry *entry = git_array_get(tree->entries, at_pos);
- if (homing_search_cmp(&ksearch, entry) < 0)
- break;
- }
-
- for (; at_pos > 0; --at_pos) {
- const git_tree_entry *entry =
- git_array_get(tree->entries, at_pos - 1);
-
- if (homing_search_cmp(&ksearch, entry) > 0)
- break;
- }
-
- return (int)at_pos;
-}
-
-size_t git_tree_entrycount(const git_tree *tree)
-{
- assert(tree);
- return tree->entries.size;
-}
-
-unsigned int git_treebuilder_entrycount(git_treebuilder *bld)
-{
- assert(bld);
-
- return git_strmap_num_entries(bld->map);
-}
-
-static int tree_error(const char *str, const char *path)
-{
- if (path)
- giterr_set(GITERR_TREE, "%s - %s", str, path);
- else
- giterr_set(GITERR_TREE, "%s", str);
- return -1;
-}
-
-static int parse_mode(unsigned int *modep, const char *buffer, const char **buffer_out)
-{
- unsigned char c;
- unsigned int mode = 0;
-
- if (*buffer == ' ')
- return -1;
-
- while ((c = *buffer++) != ' ') {
- if (c < '0' || c > '7')
- return -1;
- mode = (mode << 3) + (c - '0');
- }
- *modep = mode;
- *buffer_out = buffer;
-
- return 0;
-}
-
-int git_tree__parse(void *_tree, git_odb_object *odb_obj)
-{
- git_tree *tree = _tree;
- const char *buffer;
- const char *buffer_end;
-
- if (git_odb_object_dup(&tree->odb_obj, odb_obj) < 0)
- return -1;
-
- buffer = git_odb_object_data(tree->odb_obj);
- buffer_end = buffer + git_odb_object_size(tree->odb_obj);
-
- git_array_init_to_size(tree->entries, DEFAULT_TREE_SIZE);
- GITERR_CHECK_ARRAY(tree->entries);
-
- while (buffer < buffer_end) {
- git_tree_entry *entry;
- size_t filename_len;
- const char *nul;
- unsigned int attr;
-
- if (parse_mode(&attr, buffer, &buffer) < 0 || !buffer)
- return tree_error("Failed to parse tree. Can't parse filemode", NULL);
-
- if ((nul = memchr(buffer, 0, buffer_end - buffer)) == NULL)
- return tree_error("Failed to parse tree. Object is corrupted", NULL);
-
- if ((filename_len = nul - buffer) == 0)
- return tree_error("Failed to parse tree. Can't parse filename", NULL);
-
- if ((buffer_end - (nul + 1)) < GIT_OID_RAWSZ)
- return tree_error("Failed to parse tree. Can't parse OID", NULL);
-
- /* Allocate the entry */
- {
- entry = git_array_alloc(tree->entries);
- GITERR_CHECK_ALLOC(entry);
-
- entry->attr = attr;
- entry->filename_len = filename_len;
- entry->filename = buffer;
- entry->oid = (git_oid *) ((char *) buffer + filename_len + 1);
- }
-
- buffer += filename_len + 1;
- buffer += GIT_OID_RAWSZ;
- }
-
- return 0;
-}
-
-static size_t find_next_dir(const char *dirname, git_index *index, size_t start)
-{
- size_t dirlen, i, entries = git_index_entrycount(index);
-
- dirlen = strlen(dirname);
- for (i = start; i < entries; ++i) {
- const git_index_entry *entry = git_index_get_byindex(index, i);
- if (strlen(entry->path) < dirlen ||
- memcmp(entry->path, dirname, dirlen) ||
- (dirlen > 0 && entry->path[dirlen] != '/')) {
- break;
- }
- }
-
- return i;
-}
-
-static int append_entry(
- git_treebuilder *bld,
- const char *filename,
- const git_oid *id,
- git_filemode_t filemode)
-{
- git_tree_entry *entry;
- int error = 0;
-
- if (!valid_entry_name(bld->repo, filename))
- return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
-
- entry = alloc_entry(filename, strlen(filename), id);
- GITERR_CHECK_ALLOC(entry);
-
- entry->attr = (uint16_t)filemode;
-
- git_strmap_insert(bld->map, entry->filename, entry, error);
- if (error < 0) {
- git_tree_entry_free(entry);
- giterr_set(GITERR_TREE, "failed to append entry %s to the tree builder", filename);
- return -1;
- }
-
- return 0;
-}
-
-static int write_tree(
- git_oid *oid,
- git_repository *repo,
- git_index *index,
- const char *dirname,
- size_t start)
-{
- git_treebuilder *bld = NULL;
- size_t i, entries = git_index_entrycount(index);
- int error;
- size_t dirname_len = strlen(dirname);
- const git_tree_cache *cache;
-
- cache = git_tree_cache_get(index->tree, dirname);
- if (cache != NULL && cache->entry_count >= 0){
- git_oid_cpy(oid, &cache->oid);
- return (int)find_next_dir(dirname, index, start);
- }
-
- if ((error = git_treebuilder_new(&bld, repo, NULL)) < 0 || bld == NULL)
- return -1;
-
- /*
- * This loop is unfortunate, but necessary. The index doesn't have
- * any directores, so we need to handle that manually, and we
- * need to keep track of the current position.
- */
- for (i = start; i < entries; ++i) {
- const git_index_entry *entry = git_index_get_byindex(index, i);
- const char *filename, *next_slash;
-
- /*
- * If we've left our (sub)tree, exit the loop and return. The
- * first check is an early out (and security for the
- * third). The second check is a simple prefix comparison. The
- * third check catches situations where there is a directory
- * win32/sys and a file win32mmap.c. Without it, the following
- * code believes there is a file win32/mmap.c
- */
- if (strlen(entry->path) < dirname_len ||
- memcmp(entry->path, dirname, dirname_len) ||
- (dirname_len > 0 && entry->path[dirname_len] != '/')) {
- break;
- }
-
- filename = entry->path + dirname_len;
- if (*filename == '/')
- filename++;
- next_slash = strchr(filename, '/');
- if (next_slash) {
- git_oid sub_oid;
- int written;
- char *subdir, *last_comp;
-
- subdir = git__strndup(entry->path, next_slash - entry->path);
- GITERR_CHECK_ALLOC(subdir);
-
- /* Write out the subtree */
- written = write_tree(&sub_oid, repo, index, subdir, i);
- if (written < 0) {
- git__free(subdir);
- goto on_error;
- } else {
- i = written - 1; /* -1 because of the loop increment */
- }
-
- /*
- * We need to figure out what we want toinsert
- * into this tree. If we're traversing
- * deps/zlib/, then we only want to write
- * 'zlib' into the tree.
- */
- last_comp = strrchr(subdir, '/');
- if (last_comp) {
- last_comp++; /* Get rid of the '/' */
- } else {
- last_comp = subdir;
- }
-
- error = append_entry(bld, last_comp, &sub_oid, S_IFDIR);
- git__free(subdir);
- if (error < 0)
- goto on_error;
- } else {
- error = append_entry(bld, filename, &entry->id, entry->mode);
- if (error < 0)
- goto on_error;
- }
- }
-
- if (git_treebuilder_write(oid, bld) < 0)
- goto on_error;
-
- git_treebuilder_free(bld);
- return (int)i;
-
-on_error:
- git_treebuilder_free(bld);
- return -1;
-}
-
-int git_tree__write_index(
- git_oid *oid, git_index *index, git_repository *repo)
-{
- int ret;
- git_tree *tree;
- bool old_ignore_case = false;
-
- assert(oid && index && repo);
-
- if (git_index_has_conflicts(index)) {
- giterr_set(GITERR_INDEX,
- "Cannot create a tree from a not fully merged index.");
- return GIT_EUNMERGED;
- }
-
- if (index->tree != NULL && index->tree->entry_count >= 0) {
- git_oid_cpy(oid, &index->tree->oid);
- return 0;
- }
-
- /* The tree cache didn't help us; we'll have to write
- * out a tree. If the index is ignore_case, we must
- * make it case-sensitive for the duration of the tree-write
- * operation. */
-
- if (index->ignore_case) {
- old_ignore_case = true;
- git_index__set_ignore_case(index, false);
- }
-
- ret = write_tree(oid, repo, index, "", 0);
-
- if (old_ignore_case)
- git_index__set_ignore_case(index, true);
-
- index->tree = NULL;
-
- if (ret < 0)
- return ret;
-
- git_pool_clear(&index->tree_pool);
-
- if ((ret = git_tree_lookup(&tree, repo, oid)) < 0)
- return ret;
-
- /* Read the tree cache into the index */
- ret = git_tree_cache_read_tree(&index->tree, tree, &index->tree_pool);
- git_tree_free(tree);
-
- return ret;
-}
-
-int git_treebuilder_new(
- git_treebuilder **builder_p,
- git_repository *repo,
- const git_tree *source)
-{
- git_treebuilder *bld;
- size_t i;
-
- assert(builder_p && repo);
-
- bld = git__calloc(1, sizeof(git_treebuilder));
- GITERR_CHECK_ALLOC(bld);
-
- bld->repo = repo;
-
- if (git_strmap_alloc(&bld->map) < 0) {
- git__free(bld);
- return -1;
- }
-
- if (source != NULL) {
- git_tree_entry *entry_src;
-
- git_array_foreach(source->entries, i, entry_src) {
- if (append_entry(
- bld, entry_src->filename,
- entry_src->oid,
- entry_src->attr) < 0)
- goto on_error;
- }
- }
-
- *builder_p = bld;
- return 0;
-
-on_error:
- git_treebuilder_free(bld);
- return -1;
-}
-
-static git_otype otype_from_mode(git_filemode_t filemode)
-{
- switch (filemode) {
- case GIT_FILEMODE_TREE:
- return GIT_OBJ_TREE;
- case GIT_FILEMODE_COMMIT:
- return GIT_OBJ_COMMIT;
- default:
- return GIT_OBJ_BLOB;
- }
-}
-
-int git_treebuilder_insert(
- const git_tree_entry **entry_out,
- git_treebuilder *bld,
- const char *filename,
- const git_oid *id,
- git_filemode_t filemode)
-{
- git_tree_entry *entry;
- int error;
- git_strmap_iter pos;
-
- assert(bld && id && filename);
-
- if (!valid_filemode(filemode))
- return tree_error("Failed to insert entry. Invalid filemode for file", filename);
-
- if (!valid_entry_name(bld->repo, filename))
- return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
-
- if (filemode != GIT_FILEMODE_COMMIT &&
- !git_object__is_valid(bld->repo, id, otype_from_mode(filemode)))
- return tree_error("Failed to insert entry; invalid object specified", filename);
-
- pos = git_strmap_lookup_index(bld->map, filename);
- if (git_strmap_valid_index(bld->map, pos)) {
- entry = git_strmap_value_at(bld->map, pos);
- git_oid_cpy((git_oid *) entry->oid, id);
- } else {
- entry = alloc_entry(filename, strlen(filename), id);
- GITERR_CHECK_ALLOC(entry);
-
- git_strmap_insert(bld->map, entry->filename, entry, error);
-
- if (error < 0) {
- git_tree_entry_free(entry);
- giterr_set(GITERR_TREE, "failed to insert %s", filename);
- return -1;
- }
- }
-
- entry->attr = filemode;
-
- if (entry_out)
- *entry_out = entry;
-
- return 0;
-}
-
-static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
-{
- git_tree_entry *entry = NULL;
- git_strmap_iter pos;
-
- assert(bld && filename);
-
- pos = git_strmap_lookup_index(bld->map, filename);
- if (git_strmap_valid_index(bld->map, pos))
- entry = git_strmap_value_at(bld->map, pos);
-
- return entry;
-}
-
-const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename)
-{
- return treebuilder_get(bld, filename);
-}
-
-int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
-{
- git_tree_entry *entry = treebuilder_get(bld, filename);
-
- if (entry == NULL)
- return tree_error("Failed to remove entry. File isn't in the tree", filename);
-
- git_strmap_delete(bld->map, filename);
- git_tree_entry_free(entry);
-
- return 0;
-}
-
-int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
-{
- int error = 0;
- size_t i, entrycount;
- git_buf tree = GIT_BUF_INIT;
- git_odb *odb;
- git_tree_entry *entry;
- git_vector entries;
-
- assert(bld);
-
- entrycount = git_strmap_num_entries(bld->map);
- if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0)
- return -1;
-
- git_strmap_foreach_value(bld->map, entry, {
- if (git_vector_insert(&entries, entry) < 0)
- return -1;
- });
-
- git_vector_sort(&entries);
-
- /* Grow the buffer beforehand to an estimated size */
- error = git_buf_grow(&tree, entrycount * 72);
-
- for (i = 0; i < entries.length && !error; ++i) {
- git_tree_entry *entry = git_vector_get(&entries, i);
-
- git_buf_printf(&tree, "%o ", entry->attr);
- git_buf_put(&tree, entry->filename, entry->filename_len + 1);
- git_buf_put(&tree, (char *)entry->oid->id, GIT_OID_RAWSZ);
-
- if (git_buf_oom(&tree))
- error = -1;
- }
-
-
- if (!error &&
- !(error = git_repository_odb__weakptr(&odb, bld->repo)))
- error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
-
- git_buf_free(&tree);
- git_vector_free(&entries);
-
- return error;
-}
-
-void git_treebuilder_filter(
- git_treebuilder *bld,
- git_treebuilder_filter_cb filter,
- void *payload)
-{
- const char *filename;
- git_tree_entry *entry;
-
- assert(bld && filter);
-
- git_strmap_foreach(bld->map, filename, entry, {
- if (filter(entry, payload)) {
- git_strmap_delete(bld->map, filename);
- git_tree_entry_free(entry);
- }
- });
-}
-
-void git_treebuilder_clear(git_treebuilder *bld)
-{
- git_tree_entry *e;
-
- assert(bld);
-
- git_strmap_foreach_value(bld->map, e, git_tree_entry_free(e));
- git_strmap_clear(bld->map);
-}
-
-void git_treebuilder_free(git_treebuilder *bld)
-{
- if (bld == NULL)
- return;
-
- git_treebuilder_clear(bld);
- git_strmap_free(bld->map);
- git__free(bld);
-}
-
-static size_t subpath_len(const char *path)
-{
- const char *slash_pos = strchr(path, '/');
- if (slash_pos == NULL)
- return strlen(path);
-
- return slash_pos - path;
-}
-
-int git_tree_entry_bypath(
- git_tree_entry **entry_out,
- const git_tree *root,
- const char *path)
-{
- int error = 0;
- git_tree *subtree;
- const git_tree_entry *entry;
- size_t filename_len;
-
- /* Find how long is the current path component (i.e.
- * the filename between two slashes */
- filename_len = subpath_len(path);
-
- if (filename_len == 0) {
- giterr_set(GITERR_TREE, "Invalid tree path given");
- return GIT_ENOTFOUND;
- }
-
- entry = entry_fromname(root, path, filename_len);
-
- if (entry == NULL) {
- giterr_set(GITERR_TREE,
- "the path '%.*s' does not exist in the given tree", (int) filename_len, path);
- return GIT_ENOTFOUND;
- }
-
- switch (path[filename_len]) {
- case '/':
- /* If there are more components in the path...
- * then this entry *must* be a tree */
- if (!git_tree_entry__is_tree(entry)) {
- giterr_set(GITERR_TREE,
- "the path '%.*s' exists but is not a tree", (int) filename_len, path);
- return GIT_ENOTFOUND;
- }
-
- /* If there's only a slash left in the path, we
- * return the current entry; otherwise, we keep
- * walking down the path */
- if (path[filename_len + 1] != '\0')
- break;
-
- case '\0':
- /* If there are no more components in the path, return
- * this entry */
- return git_tree_entry_dup(entry_out, entry);
- }
-
- if (git_tree_lookup(&subtree, root->object.repo, entry->oid) < 0)
- return -1;
-
- error = git_tree_entry_bypath(
- entry_out,
- subtree,
- path + filename_len + 1
- );
-
- git_tree_free(subtree);
- return error;
-}
-
-static int tree_walk(
- const git_tree *tree,
- git_treewalk_cb callback,
- git_buf *path,
- void *payload,
- bool preorder)
-{
- int error = 0;
- size_t i;
- const git_tree_entry *entry;
-
- git_array_foreach(tree->entries, i, entry) {
- if (preorder) {
- error = callback(path->ptr, entry, payload);
- if (error < 0) { /* negative value stops iteration */
- giterr_set_after_callback_function(error, "git_tree_walk");
- break;
- }
- if (error > 0) { /* positive value skips this entry */
- error = 0;
- continue;
- }
- }
-
- if (git_tree_entry__is_tree(entry)) {
- git_tree *subtree;
- size_t path_len = git_buf_len(path);
-
- error = git_tree_lookup(&subtree, tree->object.repo, entry->oid);
- if (error < 0)
- break;
-
- /* append the next entry to the path */
- git_buf_puts(path, entry->filename);
- git_buf_putc(path, '/');
-
- if (git_buf_oom(path))
- error = -1;
- else
- error = tree_walk(subtree, callback, path, payload, preorder);
-
- git_tree_free(subtree);
- if (error != 0)
- break;
-
- git_buf_truncate(path, path_len);
- }
-
- if (!preorder) {
- error = callback(path->ptr, entry, payload);
- if (error < 0) { /* negative value stops iteration */
- giterr_set_after_callback_function(error, "git_tree_walk");
- break;
- }
- error = 0;
- }
- }
-
- return error;
-}
-
-int git_tree_walk(
- const git_tree *tree,
- git_treewalk_mode mode,
- git_treewalk_cb callback,
- void *payload)
-{
- int error = 0;
- git_buf root_path = GIT_BUF_INIT;
-
- if (mode != GIT_TREEWALK_POST && mode != GIT_TREEWALK_PRE) {
- giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");
- return -1;
- }
-
- error = tree_walk(
- tree, callback, &root_path, payload, (mode == GIT_TREEWALK_PRE));
-
- git_buf_free(&root_path);
-
- return error;
-}
-
-static int compare_entries(const void *_a, const void *_b)
-{
- const git_tree_update *a = (git_tree_update *) _a;
- const git_tree_update *b = (git_tree_update *) _b;
-
- return strcmp(a->path, b->path);
-}
-
-static int on_dup_entry(void **old, void *new)
-{
- GIT_UNUSED(old); GIT_UNUSED(new);
-
- giterr_set(GITERR_TREE, "duplicate entries given for update");
- return -1;
-}
-
-/*
- * We keep the previous tree and the new one at each level of the
- * stack. When we leave a level we're done with that tree and we can
- * write it out to the odb.
- */
-typedef struct {
- git_treebuilder *bld;
- git_tree *tree;
- char *name;
-} tree_stack_entry;
-
-/** Count how many slashes (i.e. path components) there are in this string */
-GIT_INLINE(size_t) count_slashes(const char *path)
-{
- size_t count = 0;
- const char *slash;
-
- while ((slash = strchr(path, '/')) != NULL) {
- count++;
- path = slash + 1;
- }
-
- return count;
-}
-
-static bool next_component(git_buf *out, const char *in)
-{
- const char *slash = strchr(in, '/');
-
- git_buf_clear(out);
-
- if (slash)
- git_buf_put(out, in, slash - in);
-
- return !!slash;
-}
-
-static int create_popped_tree(tree_stack_entry *current, tree_stack_entry *popped, git_buf *component)
-{
- int error;
- git_oid new_tree;
-
- git_tree_free(popped->tree);
-
- /* If the tree would be empty, remove it from the one higher up */
- if (git_treebuilder_entrycount(popped->bld) == 0) {
- git_treebuilder_free(popped->bld);
- error = git_treebuilder_remove(current->bld, popped->name);
- git__free(popped->name);
- return error;
- }
-
- error = git_treebuilder_write(&new_tree, popped->bld);
- git_treebuilder_free(popped->bld);
-
- if (error < 0) {
- git__free(popped->name);
- return error;
- }
-
- /* We've written out the tree, now we have to put the new value into its parent */
- git_buf_clear(component);
- git_buf_puts(component, popped->name);
- git__free(popped->name);
-
- GITERR_CHECK_ALLOC(component->ptr);
-
- /* Error out if this would create a D/F conflict in this update */
- if (current->tree) {
- const git_tree_entry *to_replace;
- to_replace = git_tree_entry_byname(current->tree, component->ptr);
- if (to_replace && git_tree_entry_type(to_replace) != GIT_OBJ_TREE) {
- giterr_set(GITERR_TREE, "D/F conflict when updating tree");
- return -1;
- }
- }
-
- return git_treebuilder_insert(NULL, current->bld, component->ptr, &new_tree, GIT_FILEMODE_TREE);
-}
-
-int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseline, size_t nupdates, const git_tree_update *updates)
-{
- git_array_t(tree_stack_entry) stack = GIT_ARRAY_INIT;
- tree_stack_entry *root_elem;
- git_vector entries;
- int error;
- size_t i;
- git_buf component = GIT_BUF_INIT;
-
- if ((error = git_vector_init(&entries, nupdates, compare_entries)) < 0)
- return error;
-
- /* Sort the entries for treversal */
- for (i = 0 ; i < nupdates; i++) {
- if ((error = git_vector_insert_sorted(&entries, (void *) &updates[i], on_dup_entry)) < 0)
- goto cleanup;
- }
-
- root_elem = git_array_alloc(stack);
- GITERR_CHECK_ALLOC(root_elem);
- memset(root_elem, 0, sizeof(*root_elem));
-
- if (baseline && (error = git_tree_dup(&root_elem->tree, baseline)) < 0)
- goto cleanup;
-
- if ((error = git_treebuilder_new(&root_elem->bld, repo, root_elem->tree)) < 0)
- goto cleanup;
-
- for (i = 0; i < nupdates; i++) {
- const git_tree_update *last_update = i == 0 ? NULL : git_vector_get(&entries, i-1);
- const git_tree_update *update = git_vector_get(&entries, i);
- size_t common_prefix = 0, steps_up, j;
- const char *path;
-
- /* Figure out how much we need to change from the previous tree */
- if (last_update)
- common_prefix = git_path_common_dirlen(last_update->path, update->path);
-
- /*
- * The entries are sorted, so when we find we're no
- * longer in the same directory, we need to abandon
- * the old tree (steps up) and dive down to the next
- * one.
- */
- steps_up = last_update == NULL ? 0 : count_slashes(&last_update->path[common_prefix]);
-
- for (j = 0; j < steps_up; j++) {
- tree_stack_entry *current, *popped = git_array_pop(stack);
- assert(popped);
-
- current = git_array_last(stack);
- assert(current);
-
- if ((error = create_popped_tree(current, popped, &component)) < 0)
- goto cleanup;
- }
-
- /* Now that we've created the trees we popped from the stack, let's go back down */
- path = &update->path[common_prefix];
- while (next_component(&component, path)) {
- tree_stack_entry *last, *new_entry;
- const git_tree_entry *entry;
-
- last = git_array_last(stack);
- entry = last->tree ? git_tree_entry_byname(last->tree, component.ptr) : NULL;
- if (!entry)
- entry = treebuilder_get(last->bld, component.ptr);
-
- if (entry && git_tree_entry_type(entry) != GIT_OBJ_TREE) {
- giterr_set(GITERR_TREE, "D/F conflict when updating tree");
- error = -1;
- goto cleanup;
- }
-
- new_entry = git_array_alloc(stack);
- GITERR_CHECK_ALLOC(new_entry);
- memset(new_entry, 0, sizeof(*new_entry));
-
- new_entry->tree = NULL;
- if (entry && (error = git_tree_lookup(&new_entry->tree, repo, git_tree_entry_id(entry))) < 0)
- goto cleanup;
-
- if ((error = git_treebuilder_new(&new_entry->bld, repo, new_entry->tree)) < 0)
- goto cleanup;
-
- new_entry->name = git__strdup(component.ptr);
- GITERR_CHECK_ALLOC(new_entry->name);
-
- /* Get to the start of the next component */
- path += component.size + 1;
- }
-
- /* After all that, we're finally at the place where we want to perform the update */
- switch (update->action) {
- case GIT_TREE_UPDATE_UPSERT:
- {
- /* Make sure we're replacing something of the same type */
- tree_stack_entry *last = git_array_last(stack);
- char *basename = git_path_basename(update->path);
- const git_tree_entry *e = git_treebuilder_get(last->bld, basename);
- if (e && git_tree_entry_type(e) != git_object__type_from_filemode(update->filemode)) {
- git__free(basename);
- giterr_set(GITERR_TREE, "Cannot replace '%s' with '%s' at '%s'",
- git_object_type2string(git_tree_entry_type(e)),
- git_object_type2string(git_object__type_from_filemode(update->filemode)),
- update->path);
- error = -1;
- goto cleanup;
- }
-
- error = git_treebuilder_insert(NULL, last->bld, basename, &update->id, update->filemode);
- git__free(basename);
- break;
- }
- case GIT_TREE_UPDATE_REMOVE:
- {
- char *basename = git_path_basename(update->path);
- error = git_treebuilder_remove(git_array_last(stack)->bld, basename);
- git__free(basename);
- break;
- }
- default:
- giterr_set(GITERR_TREE, "unkown action for update");
- error = -1;
- goto cleanup;
- }
-
- if (error < 0)
- goto cleanup;
- }
-
- /* We're done, go up the stack again and write out the tree */
- {
- tree_stack_entry *current = NULL, *popped = NULL;
- while ((popped = git_array_pop(stack)) != NULL) {
- current = git_array_last(stack);
- /* We've reached the top, current is the root tree */
- if (!current)
- break;
-
- if ((error = create_popped_tree(current, popped, &component)) < 0)
- goto cleanup;
- }
-
- /* Write out the root tree */
- git__free(popped->name);
- git_tree_free(popped->tree);
-
- error = git_treebuilder_write(out, popped->bld);
- git_treebuilder_free(popped->bld);
- if (error < 0)
- goto cleanup;
- }
-
-cleanup:
- {
- tree_stack_entry *e;
- while ((e = git_array_pop(stack)) != NULL) {
- git_treebuilder_free(e->bld);
- git_tree_free(e->tree);
- git__free(e->name);
- }
- }
-
- git_buf_free(&component);
- git_array_clear(stack);
- git_vector_free(&entries);
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_tree_h__
-#define INCLUDE_tree_h__
-
-#include "git2/tree.h"
-#include "repository.h"
-#include "odb.h"
-#include "vector.h"
-#include "strmap.h"
-#include "pool.h"
-
-struct git_tree_entry {
- uint16_t attr;
- uint16_t filename_len;
- const git_oid *oid;
- const char *filename;
-};
-
-struct git_tree {
- git_object object;
- git_odb_object *odb_obj;
- git_array_t(git_tree_entry) entries;
-};
-
-struct git_treebuilder {
- git_repository *repo;
- git_strmap *map;
-};
-
-GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
-{
- return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr));
-}
-
-extern int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2);
-
-void git_tree__free(void *tree);
-int git_tree__parse(void *tree, git_odb_object *obj);
-
-/**
- * Lookup the first position in the tree with a given prefix.
- *
- * @param tree a previously loaded tree.
- * @param prefix the beginning of a path to find in the tree.
- * @return index of the first item at or after the given prefix.
- */
-int git_tree__prefix_position(const git_tree *tree, const char *prefix);
-
-
-/**
- * Write a tree to the given repository
- */
-int git_tree__write_index(
- git_oid *oid, git_index *index, git_repository *repo);
-
-/**
- * Obsolete mode kept for compatibility reasons
- */
-#define GIT_FILEMODE_BLOB_GROUP_WRITABLE 0100664
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-
-/**
- * An array-of-pointers implementation of Python's Timsort
- * Based on code by Christopher Swenson under the MIT license
- *
- * Copyright (c) 2010 Christopher Swenson
- * Copyright (c) 2011 Vicent Marti
- */
-
-#ifndef MAX
-# define MAX(x,y) (((x) > (y) ? (x) : (y)))
-#endif
-
-#ifndef MIN
-# define MIN(x,y) (((x) < (y) ? (x) : (y)))
-#endif
-
-static int binsearch(
- void **dst, const void *x, size_t size, git__sort_r_cmp cmp, void *payload)
-{
- int l, c, r;
- void *lx, *cx;
-
- assert(size > 0);
-
- l = 0;
- r = (int)size - 1;
- c = r >> 1;
- lx = dst[l];
-
- /* check for beginning conditions */
- if (cmp(x, lx, payload) < 0)
- return 0;
-
- else if (cmp(x, lx, payload) == 0) {
- int i = 1;
- while (cmp(x, dst[i], payload) == 0)
- i++;
- return i;
- }
-
- /* guaranteed not to be >= rx */
- cx = dst[c];
- while (1) {
- const int val = cmp(x, cx, payload);
- if (val < 0) {
- if (c - l <= 1) return c;
- r = c;
- } else if (val > 0) {
- if (r - c <= 1) return c + 1;
- l = c;
- lx = cx;
- } else {
- do {
- cx = dst[++c];
- } while (cmp(x, cx, payload) == 0);
- return c;
- }
- c = l + ((r - l) >> 1);
- cx = dst[c];
- }
-}
-
-/* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */
-static void bisort(
- void **dst, size_t start, size_t size, git__sort_r_cmp cmp, void *payload)
-{
- size_t i;
- void *x;
- int location;
-
- for (i = start; i < size; i++) {
- int j;
- /* If this entry is already correct, just move along */
- if (cmp(dst[i - 1], dst[i], payload) <= 0)
- continue;
-
- /* Else we need to find the right place, shift everything over, and squeeze in */
- x = dst[i];
- location = binsearch(dst, x, i, cmp, payload);
- for (j = (int)i - 1; j >= location; j--) {
- dst[j + 1] = dst[j];
- }
- dst[location] = x;
- }
-}
-
-
-/* timsort implementation, based on timsort.txt */
-struct tsort_run {
- ssize_t start;
- ssize_t length;
-};
-
-struct tsort_store {
- size_t alloc;
- git__sort_r_cmp cmp;
- void *payload;
- void **storage;
-};
-
-static void reverse_elements(void **dst, ssize_t start, ssize_t end)
-{
- while (start < end) {
- void *tmp = dst[start];
- dst[start] = dst[end];
- dst[end] = tmp;
-
- start++;
- end--;
- }
-}
-
-static ssize_t count_run(
- void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
-{
- ssize_t curr = start + 2;
-
- if (size - start == 1)
- return 1;
-
- if (start >= size - 2) {
- if (store->cmp(dst[size - 2], dst[size - 1], store->payload) > 0) {
- void *tmp = dst[size - 1];
- dst[size - 1] = dst[size - 2];
- dst[size - 2] = tmp;
- }
-
- return 2;
- }
-
- if (store->cmp(dst[start], dst[start + 1], store->payload) <= 0) {
- while (curr < size - 1 &&
- store->cmp(dst[curr - 1], dst[curr], store->payload) <= 0)
- curr++;
-
- return curr - start;
- } else {
- while (curr < size - 1 &&
- store->cmp(dst[curr - 1], dst[curr], store->payload) > 0)
- curr++;
-
- /* reverse in-place */
- reverse_elements(dst, start, curr - 1);
- return curr - start;
- }
-}
-
-static size_t compute_minrun(size_t n)
-{
- int r = 0;
- while (n >= 64) {
- r |= n & 1;
- n >>= 1;
- }
- return n + r;
-}
-
-static int check_invariant(struct tsort_run *stack, ssize_t stack_curr)
-{
- if (stack_curr < 2)
- return 1;
-
- else if (stack_curr == 2) {
- const ssize_t A = stack[stack_curr - 2].length;
- const ssize_t B = stack[stack_curr - 1].length;
- return (A > B);
- } else {
- const ssize_t A = stack[stack_curr - 3].length;
- const ssize_t B = stack[stack_curr - 2].length;
- const ssize_t C = stack[stack_curr - 1].length;
- return !((A <= B + C) || (B <= C));
- }
-}
-
-static int resize(struct tsort_store *store, size_t new_size)
-{
- if (store->alloc < new_size) {
- void **tempstore;
-
- tempstore = git__reallocarray(store->storage, new_size, sizeof(void *));
-
- /**
- * Do not propagate on OOM; this will abort the sort and
- * leave the array unsorted, but no error code will be
- * raised
- */
- if (tempstore == NULL)
- return -1;
-
- store->storage = tempstore;
- store->alloc = new_size;
- }
-
- return 0;
-}
-
-static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store)
-{
- const ssize_t A = stack[stack_curr - 2].length;
- const ssize_t B = stack[stack_curr - 1].length;
- const ssize_t curr = stack[stack_curr - 2].start;
-
- void **storage;
- ssize_t i, j, k;
-
- if (resize(store, MIN(A, B)) < 0)
- return;
-
- storage = store->storage;
-
- /* left merge */
- if (A < B) {
- memcpy(storage, &dst[curr], A * sizeof(void *));
- i = 0;
- j = curr + A;
-
- for (k = curr; k < curr + A + B; k++) {
- if ((i < A) && (j < curr + A + B)) {
- if (store->cmp(storage[i], dst[j], store->payload) <= 0)
- dst[k] = storage[i++];
- else
- dst[k] = dst[j++];
- } else if (i < A) {
- dst[k] = storage[i++];
- } else
- dst[k] = dst[j++];
- }
- } else {
- memcpy(storage, &dst[curr + A], B * sizeof(void *));
- i = B - 1;
- j = curr + A - 1;
-
- for (k = curr + A + B - 1; k >= curr; k--) {
- if ((i >= 0) && (j >= curr)) {
- if (store->cmp(dst[j], storage[i], store->payload) > 0)
- dst[k] = dst[j--];
- else
- dst[k] = storage[i--];
- } else if (i >= 0)
- dst[k] = storage[i--];
- else
- dst[k] = dst[j--];
- }
- }
-}
-
-static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store, ssize_t size)
-{
- ssize_t A, B, C;
-
- while (1) {
- /* if the stack only has one thing on it, we are done with the collapse */
- if (stack_curr <= 1)
- break;
-
- /* if this is the last merge, just do it */
- if ((stack_curr == 2) && (stack[0].length + stack[1].length == size)) {
- merge(dst, stack, stack_curr, store);
- stack[0].length += stack[1].length;
- stack_curr--;
- break;
- }
-
- /* check if the invariant is off for a stack of 2 elements */
- else if ((stack_curr == 2) && (stack[0].length <= stack[1].length)) {
- merge(dst, stack, stack_curr, store);
- stack[0].length += stack[1].length;
- stack_curr--;
- break;
- }
- else if (stack_curr == 2)
- break;
-
- A = stack[stack_curr - 3].length;
- B = stack[stack_curr - 2].length;
- C = stack[stack_curr - 1].length;
-
- /* check first invariant */
- if (A <= B + C) {
- if (A < C) {
- merge(dst, stack, stack_curr - 1, store);
- stack[stack_curr - 3].length += stack[stack_curr - 2].length;
- stack[stack_curr - 2] = stack[stack_curr - 1];
- stack_curr--;
- } else {
- merge(dst, stack, stack_curr, store);
- stack[stack_curr - 2].length += stack[stack_curr - 1].length;
- stack_curr--;
- }
- } else if (B <= C) {
- merge(dst, stack, stack_curr, store);
- stack[stack_curr - 2].length += stack[stack_curr - 1].length;
- stack_curr--;
- } else
- break;
- }
-
- return stack_curr;
-}
-
-#define PUSH_NEXT() do {\
- len = count_run(dst, curr, size, store);\
- run = minrun;\
- if (run < minrun) run = minrun;\
- if (run > (ssize_t)size - curr) run = size - curr;\
- if (run > len) {\
- bisort(&dst[curr], len, run, cmp, payload);\
- len = run;\
- }\
- run_stack[stack_curr].start = curr;\
- run_stack[stack_curr++].length = len;\
- curr += len;\
- if (curr == (ssize_t)size) {\
- /* finish up */ \
- while (stack_curr > 1) { \
- merge(dst, run_stack, stack_curr, store); \
- run_stack[stack_curr - 2].length += run_stack[stack_curr - 1].length; \
- stack_curr--; \
- } \
- if (store->storage != NULL) {\
- git__free(store->storage);\
- store->storage = NULL;\
- }\
- return;\
- }\
-}\
-while (0)
-
-void git__tsort_r(
- void **dst, size_t size, git__sort_r_cmp cmp, void *payload)
-{
- struct tsort_store _store, *store = &_store;
- struct tsort_run run_stack[128];
-
- ssize_t stack_curr = 0;
- ssize_t len, run;
- ssize_t curr = 0;
- ssize_t minrun;
-
- if (size < 64) {
- bisort(dst, 1, size, cmp, payload);
- return;
- }
-
- /* compute the minimum run length */
- minrun = (ssize_t)compute_minrun(size);
-
- /* temporary storage for merges */
- store->alloc = 0;
- store->storage = NULL;
- store->cmp = cmp;
- store->payload = payload;
-
- PUSH_NEXT();
- PUSH_NEXT();
- PUSH_NEXT();
-
- while (1) {
- if (!check_invariant(run_stack, stack_curr)) {
- stack_curr = collapse(dst, run_stack, stack_curr, store, size);
- continue;
- }
-
- PUSH_NEXT();
- }
-}
-
-static int tsort_r_cmp(const void *a, const void *b, void *payload)
-{
- return ((git__tsort_cmp)payload)(a, b);
-}
-
-void git__tsort(void **dst, size_t size, git__tsort_cmp cmp)
-{
- git__tsort_r(dst, size, tsort_r_cmp, cmp);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include <git2/common.h>
-
-#if !defined(GIT_WIN32) && !defined(NO_MMAP)
-
-#include "map.h"
-#include <sys/mman.h>
-#include <unistd.h>
-#include <errno.h>
-
-int git__page_size(size_t *page_size)
-{
- long sc_page_size = sysconf(_SC_PAGE_SIZE);
- if (sc_page_size < 0) {
- giterr_set(GITERR_OS, "can't determine system page size");
- return -1;
- }
- *page_size = (size_t) sc_page_size;
- return 0;
-}
-
-int git__mmap_alignment(size_t *alignment)
-{
- return git__page_size(alignment);
-}
-
-int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
-{
- int mprot = PROT_READ;
- int mflag = 0;
-
- GIT_MMAP_VALIDATE(out, len, prot, flags);
-
- out->data = NULL;
- out->len = 0;
-
- if (prot & GIT_PROT_WRITE)
- mprot |= PROT_WRITE;
-
- if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)
- mflag = MAP_SHARED;
- else if ((flags & GIT_MAP_TYPE) == GIT_MAP_PRIVATE)
- mflag = MAP_PRIVATE;
- else
- mflag = MAP_SHARED;
-
- out->data = mmap(NULL, len, mprot, mflag, fd, offset);
-
- if (!out->data || out->data == MAP_FAILED) {
- giterr_set(GITERR_OS, "Failed to mmap. Could not write data");
- return -1;
- }
-
- out->len = len;
-
- return 0;
-}
-
-int p_munmap(git_map *map)
-{
- assert(map != NULL);
- munmap(map->data, map->len);
-
- return 0;
-}
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_posix__unix_h__
-#define INCLUDE_posix__unix_h__
-
-#include <stdio.h>
-#include <dirent.h>
-#include <sys/param.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-
-typedef int GIT_SOCKET;
-#define INVALID_SOCKET -1
-
-#define p_lseek(f,n,w) lseek(f, n, w)
-#define p_fstat(f,b) fstat(f, b)
-#define p_lstat(p,b) lstat(p,b)
-#define p_stat(p,b) stat(p, b)
-
-#if defined(GIT_USE_STAT_MTIMESPEC)
-# define st_atime_nsec st_atimespec.tv_nsec
-# define st_mtime_nsec st_mtimespec.tv_nsec
-# define st_ctime_nsec st_ctimespec.tv_nsec
-#elif defined(GIT_USE_STAT_MTIM)
-# define st_atime_nsec st_atim.tv_nsec
-# define st_mtime_nsec st_mtim.tv_nsec
-# define st_ctime_nsec st_ctim.tv_nsec
-#elif !defined(GIT_USE_STAT_MTIME_NSEC) && defined(GIT_USE_NEC)
-# error GIT_USE_NSEC defined but unknown struct stat nanosecond type
-#endif
-
-#define p_utimes(f, t) utimes(f, t)
-
-#define p_readlink(a, b, c) readlink(a, b, c)
-#define p_symlink(o,n) symlink(o, n)
-#define p_link(o,n) link(o, n)
-#define p_unlink(p) unlink(p)
-#define p_mkdir(p,m) mkdir(p, m)
-#define p_fsync(fd) fsync(fd)
-extern char *p_realpath(const char *, char *);
-
-#define p_recv(s,b,l,f) recv(s,b,l,f)
-#define p_send(s,b,l,f) send(s,b,l,f)
-#define p_inet_pton(a, b, c) inet_pton(a, b, c)
-
-#define p_strcasecmp(s1, s2) strcasecmp(s1, s2)
-#define p_strncasecmp(s1, s2, c) strncasecmp(s1, s2, c)
-#define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a)
-#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__)
-#define p_mkstemp(p) mkstemp(p)
-#define p_chdir(p) chdir(p)
-#define p_chmod(p,m) chmod(p, m)
-#define p_rmdir(p) rmdir(p)
-#define p_access(p,m) access(p,m)
-#define p_ftruncate(fd, sz) ftruncate(fd, sz)
-
-/* see win32/posix.h for explanation about why this exists */
-#define p_lstat_posixly(p,b) lstat(p,b)
-
-#define p_localtime_r(c, r) localtime_r(c, r)
-#define p_gmtime_r(c, r) gmtime_r(c, r)
-
-#define p_timeval timeval
-
-#ifdef HAVE_FUTIMENS
-GIT_INLINE(int) p_futimes(int f, const struct p_timeval t[2])
-{
- struct timespec s[2];
- s[0].tv_sec = t[0].tv_sec;
- s[0].tv_nsec = t[0].tv_usec * 1000;
- s[1].tv_sec = t[1].tv_sec;
- s[1].tv_nsec = t[1].tv_usec * 1000;
- return futimens(f, s);
-}
-#else
-# define p_futimes futimes
-#endif
-
-#ifdef HAVE_REGCOMP_L
-#include <xlocale.h>
-GIT_INLINE(int) p_regcomp(regex_t *preg, const char *pattern, int cflags)
-{
- return regcomp_l(preg, pattern, cflags, (locale_t) 0);
-}
-#else
-# define p_regcomp regcomp
-#endif
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_unix_pthread_h__
-#define INCLUDE_unix_pthread_h__
-
-typedef struct {
- pthread_t thread;
-} git_thread;
-
-#define git_threads_init() (void)0
-#define git_thread_create(git_thread_ptr, start_routine, arg) \
- pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg)
-#define git_thread_join(git_thread_ptr, status) \
- pthread_join((git_thread_ptr)->thread, status)
-#define git_thread_currentid() ((size_t)(pthread_self()))
-#define git_thread_exit(retval) pthread_exit(retval)
-
-/* Git Mutex */
-#define git_mutex pthread_mutex_t
-#define git_mutex_init(a) pthread_mutex_init(a, NULL)
-#define git_mutex_lock(a) pthread_mutex_lock(a)
-#define git_mutex_unlock(a) pthread_mutex_unlock(a)
-#define git_mutex_free(a) pthread_mutex_destroy(a)
-
-/* Git condition vars */
-#define git_cond pthread_cond_t
-#define git_cond_init(c) pthread_cond_init(c, NULL)
-#define git_cond_free(c) pthread_cond_destroy(c)
-#define git_cond_wait(c, l) pthread_cond_wait(c, l)
-#define git_cond_signal(c) pthread_cond_signal(c)
-#define git_cond_broadcast(c) pthread_cond_broadcast(c)
-
-/* Pthread (-ish) rwlock
- *
- * This differs from normal pthreads rwlocks in two ways:
- * 1. Separate APIs for releasing read locks and write locks (as
- * opposed to the pure POSIX API which only has one unlock fn)
- * 2. You should not use recursive read locks (i.e. grabbing a read
- * lock in a thread that already holds a read lock) because the
- * Windows implementation doesn't support it
- */
-#define git_rwlock pthread_rwlock_t
-#define git_rwlock_init(a) pthread_rwlock_init(a, NULL)
-#define git_rwlock_rdlock(a) pthread_rwlock_rdlock(a)
-#define git_rwlock_rdunlock(a) pthread_rwlock_unlock(a)
-#define git_rwlock_wrlock(a) pthread_rwlock_wrlock(a)
-#define git_rwlock_wrunlock(a) pthread_rwlock_unlock(a)
-#define git_rwlock_free(a) pthread_rwlock_destroy(a)
-#define GIT_RWLOCK_STATIC_INIT PTHREAD_RWLOCK_INITIALIZER
-
-#endif /* INCLUDE_unix_pthread_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include <git2/common.h>
-
-#ifndef GIT_WIN32
-
-#include <limits.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-char *p_realpath(const char *pathname, char *resolved)
-{
- char *ret;
- if ((ret = realpath(pathname, resolved)) == NULL)
- return NULL;
-
-#ifdef __OpenBSD__
- /* The OpenBSD realpath function behaves differently,
- * figure out if the file exists */
- if (access(ret, F_OK) < 0)
- ret = NULL;
-#endif
- return ret;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_userdiff_h__
-#define INCLUDE_userdiff_h__
-
-/*
- * This file isolates the built in diff driver function name patterns.
- * Most of these patterns are taken from Git (with permission from the
- * original authors for relicensing to libgit2).
- */
-
-typedef struct {
- const char *name;
- const char *fns;
- const char *words;
- int flags;
-} git_diff_driver_definition;
-
-#define WORD_DEFAULT "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+"
-
-/*
- * These builtin driver definition macros have same signature as in core
- * git userdiff.c so that the data can be extracted verbatim
- */
-#define PATTERNS(NAME, FN_PATS, WORD_PAT) \
- { NAME, FN_PATS, WORD_PAT WORD_DEFAULT, 0 }
-#define IPATTERN(NAME, FN_PATS, WORD_PAT) \
- { NAME, FN_PATS, WORD_PAT WORD_DEFAULT, REG_ICASE }
-
-/*
- * The table of diff driver patterns
- *
- * Function name patterns are a list of newline separated patterns that
- * match a function declaration (i.e. the line you want in the hunk header),
- * or a negative pattern prefixed with a '!' to reject a pattern (such as
- * rejecting goto labels in C code).
- *
- * Word boundary patterns are just a simple pattern that will be OR'ed with
- * the default value above (i.e. whitespace or non-ASCII characters).
- */
-static git_diff_driver_definition builtin_defs[] = {
-
-IPATTERN("ada",
- "!^(.*[ \t])?(is[ \t]+new|renames|is[ \t]+separate)([ \t].*)?$\n"
- "!^[ \t]*with[ \t].*$\n"
- "^[ \t]*((procedure|function)[ \t]+.*)$\n"
- "^[ \t]*((package|protected|task)[ \t]+.*)$",
- /* -- */
- "[a-zA-Z][a-zA-Z0-9_]*"
- "|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?"
- "|=>|\\.\\.|\\*\\*|:=|/=|>=|<=|<<|>>|<>"),
-
-IPATTERN("fortran",
- "!^([C*]|[ \t]*!)\n"
- "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n"
- "^[ \t]*((END[ \t]+)?(PROGRAM|MODULE|BLOCK[ \t]+DATA"
- "|([^'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$",
- /* -- */
- "[a-zA-Z][a-zA-Z0-9_]*"
- "|\\.([Ee][Qq]|[Nn][Ee]|[Gg][TtEe]|[Ll][TtEe]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Aa][Nn][Dd]|[Oo][Rr]|[Nn]?[Ee][Qq][Vv]|[Nn][Oo][Tt])\\."
- /* numbers and format statements like 2E14.4, or ES12.6, 9X.
- * Don't worry about format statements without leading digits since
- * they would have been matched above as a variable anyway. */
- "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?"
- "|//|\\*\\*|::|[/<>=]="),
-
-PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
- "[^<>= \t]+"),
-
-PATTERNS("java",
- "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
- "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$",
- /* -- */
- "[a-zA-Z_][a-zA-Z0-9_]*"
- "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
- "|[-+*/<>%&^|=!]="
- "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"),
-
-PATTERNS("matlab",
- "^[[:space:]]*((classdef|function)[[:space:]].*)$|^%%[[:space:]].*$",
- "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"),
-
-PATTERNS("objc",
- /* Negate C statements that can look like functions */
- "!^[ \t]*(do|for|if|else|return|switch|while)\n"
- /* Objective-C methods */
- "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
- /* C functions */
- "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$\n"
- /* Objective-C class/protocol definitions */
- "^(@(implementation|interface|protocol)[ \t].*)$",
- /* -- */
- "[a-zA-Z_][a-zA-Z0-9_]*"
- "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
- "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
-
-PATTERNS("pascal",
- "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|"
- "implementation|initialization|finalization)[ \t]*.*)$"
- "\n"
- "^(.*=[ \t]*(class|record).*)$",
- /* -- */
- "[a-zA-Z_][a-zA-Z0-9_]*"
- "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
- "|<>|<=|>=|:=|\\.\\."),
-
-PATTERNS("perl",
- "^package .*\n"
- "^sub [[:alnum:]_':]+[ \t]*"
- "(\\([^)]*\\)[ \t]*)?" /* prototype */
- /*
- * Attributes. A regex can't count nested parentheses,
- * so just slurp up whatever we see, taking care not
- * to accept lines like "sub foo; # defined elsewhere".
- *
- * An attribute could contain a semicolon, but at that
- * point it seems reasonable enough to give up.
- */
- "(:[^;#]*)?"
- "(\\{[ \t]*)?" /* brace can come here or on the next line */
- "(#.*)?$\n" /* comment */
- "^(BEGIN|END|INIT|CHECK|UNITCHECK|AUTOLOAD|DESTROY)[ \t]*"
- "(\\{[ \t]*)?" /* brace can come here or on the next line */
- "(#.*)?$\n"
- "^=head[0-9] .*", /* POD */
- /* -- */
- "[[:alpha:]_'][[:alnum:]_']*"
- "|0[xb]?[0-9a-fA-F_]*"
- /* taking care not to interpret 3..5 as (3.)(.5) */
- "|[0-9a-fA-F_]+(\\.[0-9a-fA-F_]+)?([eE][-+]?[0-9_]+)?"
- "|=>|-[rwxoRWXOezsfdlpSugkbctTBMAC>]|~~|::"
- "|&&=|\\|\\|=|//=|\\*\\*="
- "|&&|\\|\\||//|\\+\\+|--|\\*\\*|\\.\\.\\.?"
- "|[-+*/%.^&<>=!|]="
- "|=~|!~"
- "|<<|<>|<=>|>>"),
-
-PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$",
- /* -- */
- "[a-zA-Z_][a-zA-Z0-9_]*"
- "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
- "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"),
-
-PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
- /* -- */
- "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
- "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
- "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"),
-
-PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
- "[={}\"]|[^={}\" \t]+"),
-
-PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
- "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
-
-PATTERNS("cpp",
- /* Jump targets or access declarations */
- "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n"
- /* functions/methods, variables, and compounds at top level */
- "^((::[[:space:]]*)?[A-Za-z_].*)$",
- /* -- */
- "[a-zA-Z_][a-zA-Z0-9_]*"
- "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*"
- "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"),
-
-PATTERNS("csharp",
- /* Keywords */
- "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
- /* Methods and constructors */
- "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
- /* Properties */
- "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
- /* Type definitions */
- "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
- /* Namespace */
- "^[ \t]*(namespace[ \t]+.*)$",
- /* -- */
- "[a-zA-Z_][a-zA-Z0-9_]*"
- "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
- "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
-
-PATTERNS("php",
- "^[ \t]*(((public|private|protected|static|final)[ \t]+)*((class|function)[ \t].*))$",
- /* -- */
- "[a-zA-Z_][a-zA-Z0-9_]*"
- "|[-+0-9.e]+[fFlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
- "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
-
-PATTERNS("javascript",
- "([a-zA-Z_$][a-zA-Z0-9_$]*(\\.[a-zA-Z0-9_$]+)*[ \t]*=[ \t]*function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)\n"
- "([a-zA-Z_$][a-zA-Z0-9_$]*[ \t]*:[ \t]*function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)\n"
- "[^a-zA-Z0-9_\\$](function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)",
- /* -- */
- "[a-zA-Z_][a-zA-Z0-9_]*"
- "|[-+0-9.e]+[fFlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
- "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
-};
-
-#undef IPATTERN
-#undef PATTERNS
-#undef WORD_DEFAULT
-
-#endif
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include <git2.h>
-#include "common.h"
-#include <stdio.h>
-#include <ctype.h>
-#include "posix.h"
-
-#ifdef GIT_WIN32
-# include "win32/w32_buffer.h"
-#endif
-
-#ifdef _MSC_VER
-# include <Shlwapi.h>
-#endif
-
-void git_strarray_free(git_strarray *array)
-{
- size_t i;
-
- if (array == NULL)
- return;
-
- for (i = 0; i < array->count; ++i)
- git__free(array->strings[i]);
-
- git__free(array->strings);
-
- memset(array, 0, sizeof(*array));
-}
-
-int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
-{
- size_t i;
-
- assert(tgt && src);
-
- memset(tgt, 0, sizeof(*tgt));
-
- if (!src->count)
- return 0;
-
- tgt->strings = git__calloc(src->count, sizeof(char *));
- GITERR_CHECK_ALLOC(tgt->strings);
-
- for (i = 0; i < src->count; ++i) {
- if (!src->strings[i])
- continue;
-
- tgt->strings[tgt->count] = git__strdup(src->strings[i]);
- if (!tgt->strings[tgt->count]) {
- git_strarray_free(tgt);
- memset(tgt, 0, sizeof(*tgt));
- return -1;
- }
-
- tgt->count++;
- }
-
- return 0;
-}
-
-int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int base)
-{
-
- return git__strntol64(result, nptr, (size_t)-1, endptr, base);
-}
-
-int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
-{
- const char *p;
- int64_t n, nn;
- int c, ovfl, v, neg, ndig;
-
- p = nptr;
- neg = 0;
- n = 0;
- ndig = 0;
- ovfl = 0;
-
- /*
- * White space
- */
- while (git__isspace(*p))
- p++;
-
- /*
- * Sign
- */
- if (*p == '-' || *p == '+')
- if (*p++ == '-')
- neg = 1;
-
- /*
- * Base
- */
- if (base == 0) {
- if (*p != '0')
- base = 10;
- else {
- base = 8;
- if (p[1] == 'x' || p[1] == 'X') {
- p += 2;
- base = 16;
- }
- }
- } else if (base == 16 && *p == '0') {
- if (p[1] == 'x' || p[1] == 'X')
- p += 2;
- } else if (base < 0 || 36 < base)
- goto Return;
-
- /*
- * Non-empty sequence of digits
- */
- for (; nptr_len > 0; p++,ndig++,nptr_len--) {
- c = *p;
- v = base;
- if ('0'<=c && c<='9')
- v = c - '0';
- else if ('a'<=c && c<='z')
- v = c - 'a' + 10;
- else if ('A'<=c && c<='Z')
- v = c - 'A' + 10;
- if (v >= base)
- break;
- nn = n * base + (neg ? -v : v);
- if ((!neg && nn < n) || (neg && nn > n))
- ovfl = 1;
- n = nn;
- }
-
-Return:
- if (ndig == 0) {
- giterr_set(GITERR_INVALID, "Failed to convert string to long. Not a number");
- return -1;
- }
-
- if (endptr)
- *endptr = p;
-
- if (ovfl) {
- giterr_set(GITERR_INVALID, "Failed to convert string to long. Overflow error");
- return -1;
- }
-
- *result = n;
- return 0;
-}
-
-int git__strtol32(int32_t *result, const char *nptr, const char **endptr, int base)
-{
-
- return git__strntol32(result, nptr, (size_t)-1, endptr, base);
-}
-
-int git__strntol32(int32_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
-{
- int error;
- int32_t tmp_int;
- int64_t tmp_long;
-
- if ((error = git__strntol64(&tmp_long, nptr, nptr_len, endptr, base)) < 0)
- return error;
-
- tmp_int = tmp_long & 0xFFFFFFFF;
- if (tmp_int != tmp_long) {
- giterr_set(GITERR_INVALID, "Failed to convert. '%s' is too large", nptr);
- return -1;
- }
-
- *result = tmp_int;
-
- return error;
-}
-
-int git__strcmp(const char *a, const char *b)
-{
- while (*a && *b && *a == *b)
- ++a, ++b;
- return (int)(*(const unsigned char *)a) - (int)(*(const unsigned char *)b);
-}
-
-int git__strcasecmp(const char *a, const char *b)
-{
- while (*a && *b && git__tolower(*a) == git__tolower(*b))
- ++a, ++b;
- return ((unsigned char)git__tolower(*a) - (unsigned char)git__tolower(*b));
-}
-
-int git__strcasesort_cmp(const char *a, const char *b)
-{
- int cmp = 0;
-
- while (*a && *b) {
- if (*a != *b) {
- if (git__tolower(*a) != git__tolower(*b))
- break;
- /* use case in sort order even if not in equivalence */
- if (!cmp)
- cmp = (int)(*(const uint8_t *)a) - (int)(*(const uint8_t *)b);
- }
-
- ++a, ++b;
- }
-
- if (*a || *b)
- return (unsigned char)git__tolower(*a) - (unsigned char)git__tolower(*b);
-
- return cmp;
-}
-
-int git__strncmp(const char *a, const char *b, size_t sz)
-{
- while (sz && *a && *b && *a == *b)
- --sz, ++a, ++b;
- if (!sz)
- return 0;
- return (int)(*(const unsigned char *)a) - (int)(*(const unsigned char *)b);
-}
-
-int git__strncasecmp(const char *a, const char *b, size_t sz)
-{
- int al, bl;
-
- do {
- al = (unsigned char)git__tolower(*a);
- bl = (unsigned char)git__tolower(*b);
- ++a, ++b;
- } while (--sz && al && al == bl);
-
- return al - bl;
-}
-
-void git__strntolower(char *str, size_t len)
-{
- size_t i;
-
- for (i = 0; i < len; ++i) {
- str[i] = (char)git__tolower(str[i]);
- }
-}
-
-void git__strtolower(char *str)
-{
- git__strntolower(str, strlen(str));
-}
-
-int git__prefixcmp(const char *str, const char *prefix)
-{
- for (;;) {
- unsigned char p = *(prefix++), s;
- if (!p)
- return 0;
- if ((s = *(str++)) != p)
- return s - p;
- }
-}
-
-int git__prefixcmp_icase(const char *str, const char *prefix)
-{
- return strncasecmp(str, prefix, strlen(prefix));
-}
-
-int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
-{
- int s, p;
-
- while(str_n--) {
- s = (unsigned char)git__tolower(*str++);
- p = (unsigned char)git__tolower(*prefix++);
-
- if (s != p)
- return s - p;
- }
-
- return (0 - *prefix);
-}
-
-int git__suffixcmp(const char *str, const char *suffix)
-{
- size_t a = strlen(str);
- size_t b = strlen(suffix);
- if (a < b)
- return -1;
- return strcmp(str + (a - b), suffix);
-}
-
-char *git__strtok(char **end, const char *sep)
-{
- char *ptr = *end;
-
- while (*ptr && strchr(sep, *ptr))
- ++ptr;
-
- if (*ptr) {
- char *start = ptr;
- *end = start + 1;
-
- while (**end && !strchr(sep, **end))
- ++*end;
-
- if (**end) {
- **end = '\0';
- ++*end;
- }
-
- return start;
- }
-
- return NULL;
-}
-
-/* Similar to strtok, but does not collapse repeated tokens. */
-char *git__strsep(char **end, const char *sep)
-{
- char *start = *end, *ptr = *end;
-
- while (*ptr && !strchr(sep, *ptr))
- ++ptr;
-
- if (*ptr) {
- *end = ptr + 1;
- *ptr = '\0';
-
- return start;
- }
-
- return NULL;
-}
-
-size_t git__linenlen(const char *buffer, size_t buffer_len)
-{
- char *nl = memchr(buffer, '\n', buffer_len);
- return nl ? (size_t)(nl - buffer) + 1 : buffer_len;
-}
-
-void git__hexdump(const char *buffer, size_t len)
-{
- static const size_t LINE_WIDTH = 16;
-
- size_t line_count, last_line, i, j;
- const char *line;
-
- line_count = (len / LINE_WIDTH);
- last_line = (len % LINE_WIDTH);
-
- for (i = 0; i < line_count; ++i) {
- line = buffer + (i * LINE_WIDTH);
- for (j = 0; j < LINE_WIDTH; ++j, ++line)
- printf("%02X ", (unsigned char)*line & 0xFF);
-
- printf("| ");
-
- line = buffer + (i * LINE_WIDTH);
- for (j = 0; j < LINE_WIDTH; ++j, ++line)
- printf("%c", (*line >= 32 && *line <= 126) ? *line : '.');
-
- printf("\n");
- }
-
- if (last_line > 0) {
-
- line = buffer + (line_count * LINE_WIDTH);
- for (j = 0; j < last_line; ++j, ++line)
- printf("%02X ", (unsigned char)*line & 0xFF);
-
- for (j = 0; j < (LINE_WIDTH - last_line); ++j)
- printf(" ");
-
- printf("| ");
-
- line = buffer + (line_count * LINE_WIDTH);
- for (j = 0; j < last_line; ++j, ++line)
- printf("%c", (*line >= 32 && *line <= 126) ? *line : '.');
-
- printf("\n");
- }
-
- printf("\n");
-}
-
-#ifdef GIT_LEGACY_HASH
-uint32_t git__hash(const void *key, int len, unsigned int seed)
-{
- const uint32_t m = 0x5bd1e995;
- const int r = 24;
- uint32_t h = seed ^ len;
-
- const unsigned char *data = (const unsigned char *)key;
-
- while(len >= 4) {
- uint32_t k = *(uint32_t *)data;
-
- k *= m;
- k ^= k >> r;
- k *= m;
-
- h *= m;
- h ^= k;
-
- data += 4;
- len -= 4;
- }
-
- switch(len) {
- case 3: h ^= data[2] << 16;
- case 2: h ^= data[1] << 8;
- case 1: h ^= data[0];
- h *= m;
- };
-
- h ^= h >> 13;
- h *= m;
- h ^= h >> 15;
-
- return h;
-}
-#else
-/*
- Cross-platform version of Murmurhash3
- http://code.google.com/p/smhasher/wiki/MurmurHash3
- by Austin Appleby (aappleby@gmail.com)
-
- This code is on the public domain.
-*/
-uint32_t git__hash(const void *key, int len, uint32_t seed)
-{
-
-#define MURMUR_BLOCK() {\
- k1 *= c1; \
- k1 = git__rotl(k1,11);\
- k1 *= c2;\
- h1 ^= k1;\
- h1 = h1*3 + 0x52dce729;\
- c1 = c1*5 + 0x7b7d159c;\
- c2 = c2*5 + 0x6bce6396;\
-}
-
- const uint8_t *data = (const uint8_t*)key;
- const int nblocks = len / 4;
-
- const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4);
- const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
-
- uint32_t h1 = 0x971e137b ^ seed;
- uint32_t k1;
-
- uint32_t c1 = 0x95543787;
- uint32_t c2 = 0x2ad7eb25;
-
- int i;
-
- for (i = -nblocks; i; i++) {
- k1 = blocks[i];
- MURMUR_BLOCK();
- }
-
- k1 = 0;
-
- switch(len & 3) {
- case 3: k1 ^= tail[2] << 16;
- case 2: k1 ^= tail[1] << 8;
- case 1: k1 ^= tail[0];
- MURMUR_BLOCK();
- }
-
- h1 ^= len;
- h1 ^= h1 >> 16;
- h1 *= 0x85ebca6b;
- h1 ^= h1 >> 13;
- h1 *= 0xc2b2ae35;
- h1 ^= h1 >> 16;
-
- return h1;
-}
-#endif
-
-/**
- * A modified `bsearch` from the BSD glibc.
- *
- * Copyright (c) 1990 Regents of the University of California.
- * All rights reserved.
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. [rescinded 22 July 1999]
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-int git__bsearch(
- void **array,
- size_t array_len,
- const void *key,
- int (*compare)(const void *, const void *),
- size_t *position)
-{
- size_t lim;
- int cmp = -1;
- void **part, **base = array;
-
- for (lim = array_len; lim != 0; lim >>= 1) {
- part = base + (lim >> 1);
- cmp = (*compare)(key, *part);
- if (cmp == 0) {
- base = part;
- break;
- }
- if (cmp > 0) { /* key > p; take right partition */
- base = part + 1;
- lim--;
- } /* else take left partition */
- }
-
- if (position)
- *position = (base - array);
-
- return (cmp == 0) ? 0 : GIT_ENOTFOUND;
-}
-
-int git__bsearch_r(
- void **array,
- size_t array_len,
- const void *key,
- int (*compare_r)(const void *, const void *, void *),
- void *payload,
- size_t *position)
-{
- size_t lim;
- int cmp = -1;
- void **part, **base = array;
-
- for (lim = array_len; lim != 0; lim >>= 1) {
- part = base + (lim >> 1);
- cmp = (*compare_r)(key, *part, payload);
- if (cmp == 0) {
- base = part;
- break;
- }
- if (cmp > 0) { /* key > p; take right partition */
- base = part + 1;
- lim--;
- } /* else take left partition */
- }
-
- if (position)
- *position = (base - array);
-
- return (cmp == 0) ? 0 : GIT_ENOTFOUND;
-}
-
-/**
- * A strcmp wrapper
- *
- * We don't want direct pointers to the CRT on Windows, we may
- * get stdcall conflicts.
- */
-int git__strcmp_cb(const void *a, const void *b)
-{
- return strcmp((const char *)a, (const char *)b);
-}
-
-int git__strcasecmp_cb(const void *a, const void *b)
-{
- return strcasecmp((const char *)a, (const char *)b);
-}
-
-int git__parse_bool(int *out, const char *value)
-{
- /* A missing value means true */
- if (value == NULL ||
- !strcasecmp(value, "true") ||
- !strcasecmp(value, "yes") ||
- !strcasecmp(value, "on")) {
- *out = 1;
- return 0;
- }
- if (!strcasecmp(value, "false") ||
- !strcasecmp(value, "no") ||
- !strcasecmp(value, "off") ||
- value[0] == '\0') {
- *out = 0;
- return 0;
- }
-
- return -1;
-}
-
-size_t git__unescape(char *str)
-{
- char *scan, *pos = str;
-
- if (!str)
- return 0;
-
- for (scan = str; *scan; pos++, scan++) {
- if (*scan == '\\' && *(scan + 1) != '\0')
- scan++; /* skip '\' but include next char */
- if (pos != scan)
- *pos = *scan;
- }
-
- if (pos != scan) {
- *pos = '\0';
- }
-
- return (pos - str);
-}
-
-#if defined(HAVE_QSORT_S) || (defined(HAVE_QSORT_R) && defined(BSD))
-typedef struct {
- git__sort_r_cmp cmp;
- void *payload;
-} git__qsort_r_glue;
-
-static int GIT_STDLIB_CALL git__qsort_r_glue_cmp(
- void *payload, const void *a, const void *b)
-{
- git__qsort_r_glue *glue = payload;
- return glue->cmp(a, b, glue->payload);
-}
-#endif
-
-void git__qsort_r(
- void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
-{
-#if defined(HAVE_QSORT_R) && defined(BSD)
- git__qsort_r_glue glue = { cmp, payload };
- qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp);
-#elif defined(HAVE_QSORT_R) && defined(__GLIBC__)
- qsort_r(els, nel, elsize, cmp, payload);
-#elif defined(HAVE_QSORT_S)
- git__qsort_r_glue glue = { cmp, payload };
- qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue);
-#else
- git__insertsort_r(els, nel, elsize, NULL, cmp, payload);
-#endif
-}
-
-void git__insertsort_r(
- void *els, size_t nel, size_t elsize, void *swapel,
- git__sort_r_cmp cmp, void *payload)
-{
- uint8_t *base = els;
- uint8_t *end = base + nel * elsize;
- uint8_t *i, *j;
- bool freeswap = !swapel;
-
- if (freeswap)
- swapel = git__malloc(elsize);
-
- for (i = base + elsize; i < end; i += elsize)
- for (j = i; j > base && cmp(j, j - elsize, payload) < 0; j -= elsize) {
- memcpy(swapel, j, elsize);
- memcpy(j, j - elsize, elsize);
- memcpy(j - elsize, swapel, elsize);
- }
-
- if (freeswap)
- git__free(swapel);
-}
-
-/*
- * git__utf8_iterate is taken from the utf8proc project,
- * http://www.public-software-group.org/utf8proc
- *
- * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the ""Software""),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-static const int8_t utf8proc_utf8class[256] = {
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-int git__utf8_charlen(const uint8_t *str, int str_len)
-{
- int length, i;
-
- length = utf8proc_utf8class[str[0]];
- if (!length)
- return -1;
-
- if (str_len >= 0 && length > str_len)
- return -str_len;
-
- for (i = 1; i < length; i++) {
- if ((str[i] & 0xC0) != 0x80)
- return -i;
- }
-
- return length;
-}
-
-int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst)
-{
- int length;
- int32_t uc = -1;
-
- *dst = -1;
- length = git__utf8_charlen(str, str_len);
- if (length < 0)
- return -1;
-
- switch (length) {
- case 1:
- uc = str[0];
- break;
- case 2:
- uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
- if (uc < 0x80) uc = -1;
- break;
- case 3:
- uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6)
- + (str[2] & 0x3F);
- if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
- (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
- break;
- case 4:
- uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
- + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
- if (uc < 0x10000 || uc >= 0x110000) uc = -1;
- break;
- }
-
- if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE))
- return -1;
-
- *dst = uc;
- return length;
-}
-
-double git_time_monotonic(void)
-{
- return git__timer();
-}
-
-#ifdef GIT_WIN32
-int git__getenv(git_buf *out, const char *name)
-{
- wchar_t *wide_name = NULL, *wide_value = NULL;
- DWORD value_len;
- int error = -1;
-
- git_buf_clear(out);
-
- if (git__utf8_to_16_alloc(&wide_name, name) < 0)
- return -1;
-
- if ((value_len = GetEnvironmentVariableW(wide_name, NULL, 0)) > 0) {
- wide_value = git__malloc(value_len * sizeof(wchar_t));
- GITERR_CHECK_ALLOC(wide_value);
-
- value_len = GetEnvironmentVariableW(wide_name, wide_value, value_len);
- }
-
- if (value_len)
- error = git_buf_put_w(out, wide_value, value_len);
- else if (GetLastError() == ERROR_ENVVAR_NOT_FOUND)
- error = GIT_ENOTFOUND;
- else
- giterr_set(GITERR_OS, "could not read environment variable '%s'", name);
-
- git__free(wide_name);
- git__free(wide_value);
- return error;
-}
-#else
-int git__getenv(git_buf *out, const char *name)
-{
- const char *val = getenv(name);
-
- git_buf_clear(out);
-
- if (!val)
- return GIT_ENOTFOUND;
-
- return git_buf_puts(out, val);
-}
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_util_h__
-#define INCLUDE_util_h__
-
-#include "git2/buffer.h"
-#include "buffer.h"
-
-#if defined(GIT_MSVC_CRTDBG)
-/* Enable MSVC CRTDBG memory leak reporting.
- *
- * We DO NOT use the "_CRTDBG_MAP_ALLOC" macro described in the MSVC
- * documentation because all allocs/frees in libgit2 already go through
- * the "git__" routines defined in this file. Simply using the normal
- * reporting mechanism causes all leaks to be attributed to a routine
- * here in util.h (ie, the actual call to calloc()) rather than the
- * caller of git__calloc().
- *
- * Therefore, we declare a set of "git__crtdbg__" routines to replace
- * the corresponding "git__" routines and re-define the "git__" symbols
- * as macros. This allows us to get and report the file:line info of
- * the real caller.
- *
- * We DO NOT replace the "git__free" routine because it needs to remain
- * a function pointer because it is used as a function argument when
- * setting up various structure "destructors".
- *
- * We also DO NOT use the "_CRTDBG_MAP_ALLOC" macro because it causes
- * "free" to be remapped to "_free_dbg" and this causes problems for
- * structures which define a field named "free".
- *
- * Finally, CRTDBG must be explicitly enabled and configured at program
- * startup. See tests/main.c for an example.
- */
-#include <stdlib.h>
-#include <crtdbg.h>
-#include "win32/w32_crtdbg_stacktrace.h"
-#endif
-
-#include "common.h"
-#include "strnlen.h"
-
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
-#define bitsizeof(x) (CHAR_BIT * sizeof(x))
-#define MSB(x, bits) ((x) & (~0ULL << (bitsizeof(x) - (bits))))
-#ifndef min
-# define min(a,b) ((a) < (b) ? (a) : (b))
-#endif
-#ifndef max
-# define max(a,b) ((a) > (b) ? (a) : (b))
-#endif
-
-#define GIT_DATE_RFC2822_SZ 32
-
-/**
- * Return the length of a constant string.
- * We are aware that `strlen` performs the same task and is usually
- * optimized away by the compiler, whilst being safer because it returns
- * valid values when passed a pointer instead of a constant string; however
- * this macro will transparently work with wide-char and single-char strings.
- */
-#define CONST_STRLEN(x) ((sizeof(x)/sizeof(x[0])) - 1)
-
-#if defined(GIT_MSVC_CRTDBG)
-
-GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line)
-{
- void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!ptr) giterr_set_oom();
- return ptr;
-}
-
-GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line)
-{
- void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!ptr) giterr_set_oom();
- return ptr;
-}
-
-GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line)
-{
- char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!ptr) giterr_set_oom();
- return ptr;
-}
-
-GIT_INLINE(char *) git__crtdbg__strndup(const char *str, size_t n, const char *file, int line)
-{
- size_t length = 0, alloclength;
- char *ptr;
-
- length = p_strnlen(str, n);
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
- !(ptr = git__crtdbg__malloc(alloclength, file, line)))
- return NULL;
-
- if (length)
- memcpy(ptr, str, length);
-
- ptr[length] = '\0';
-
- return ptr;
-}
-
-GIT_INLINE(char *) git__crtdbg__substrdup(const char *start, size_t n, const char *file, int line)
-{
- char *ptr;
- size_t alloclen;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
- !(ptr = git__crtdbg__malloc(alloclen, file, line)))
- return NULL;
-
- memcpy(ptr, start, n);
- ptr[n] = '\0';
- return ptr;
-}
-
-GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line)
-{
- void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
- if (!new_ptr) giterr_set_oom();
- return new_ptr;
-}
-
-GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
-{
- size_t newsize;
-
- return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
- NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
-}
-
-GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
-{
- return git__crtdbg__reallocarray(NULL, nelem, elsize, file, line);
-}
-
-#define git__malloc(len) git__crtdbg__malloc(len, __FILE__, __LINE__)
-#define git__calloc(nelem, elsize) git__crtdbg__calloc(nelem, elsize, __FILE__, __LINE__)
-#define git__strdup(str) git__crtdbg__strdup(str, __FILE__, __LINE__)
-#define git__strndup(str, n) git__crtdbg__strndup(str, n, __FILE__, __LINE__)
-#define git__substrdup(str, n) git__crtdbg__substrdup(str, n, __FILE__, __LINE__)
-#define git__realloc(ptr, size) git__crtdbg__realloc(ptr, size, __FILE__, __LINE__)
-#define git__reallocarray(ptr, nelem, elsize) git__crtdbg__reallocarray(ptr, nelem, elsize, __FILE__, __LINE__)
-#define git__mallocarray(nelem, elsize) git__crtdbg__mallocarray(nelem, elsize, __FILE__, __LINE__)
-
-#else
-
-/*
- * Custom memory allocation wrappers
- * that set error code and error message
- * on allocation failure
- */
-GIT_INLINE(void *) git__malloc(size_t len)
-{
- void *ptr = malloc(len);
- if (!ptr) giterr_set_oom();
- return ptr;
-}
-
-GIT_INLINE(void *) git__calloc(size_t nelem, size_t elsize)
-{
- void *ptr = calloc(nelem, elsize);
- if (!ptr) giterr_set_oom();
- return ptr;
-}
-
-GIT_INLINE(char *) git__strdup(const char *str)
-{
- char *ptr = strdup(str);
- if (!ptr) giterr_set_oom();
- return ptr;
-}
-
-GIT_INLINE(char *) git__strndup(const char *str, size_t n)
-{
- size_t length = 0, alloclength;
- char *ptr;
-
- length = p_strnlen(str, n);
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
- !(ptr = git__malloc(alloclength)))
- return NULL;
-
- if (length)
- memcpy(ptr, str, length);
-
- ptr[length] = '\0';
-
- return ptr;
-}
-
-/* NOTE: This doesn't do null or '\0' checking. Watch those boundaries! */
-GIT_INLINE(char *) git__substrdup(const char *start, size_t n)
-{
- char *ptr;
- size_t alloclen;
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
- !(ptr = git__malloc(alloclen)))
- return NULL;
-
- memcpy(ptr, start, n);
- ptr[n] = '\0';
- return ptr;
-}
-
-GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
-{
- void *new_ptr = realloc(ptr, size);
- if (!new_ptr) giterr_set_oom();
- return new_ptr;
-}
-
-/**
- * Similar to `git__realloc`, except that it is suitable for reallocing an
- * array to a new number of elements of `nelem`, each of size `elsize`.
- * The total size calculation is checked for overflow.
- */
-GIT_INLINE(void *) git__reallocarray(void *ptr, size_t nelem, size_t elsize)
-{
- size_t newsize;
- return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
- NULL : realloc(ptr, newsize);
-}
-
-/**
- * Similar to `git__calloc`, except that it does not zero memory.
- */
-GIT_INLINE(void *) git__mallocarray(size_t nelem, size_t elsize)
-{
- return git__reallocarray(NULL, nelem, elsize);
-}
-
-#endif /* !MSVC_CTRDBG */
-
-GIT_INLINE(void) git__free(void *ptr)
-{
- free(ptr);
-}
-
-#define STRCMP_CASESELECT(IGNORE_CASE, STR1, STR2) \
- ((IGNORE_CASE) ? strcasecmp((STR1), (STR2)) : strcmp((STR1), (STR2)))
-
-#define CASESELECT(IGNORE_CASE, ICASE, CASE) \
- ((IGNORE_CASE) ? (ICASE) : (CASE))
-
-extern int git__prefixcmp(const char *str, const char *prefix);
-extern int git__prefixcmp_icase(const char *str, const char *prefix);
-extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix);
-extern int git__suffixcmp(const char *str, const char *suffix);
-
-GIT_INLINE(int) git__signum(int val)
-{
- return ((val > 0) - (val < 0));
-}
-
-extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base);
-extern int git__strntol32(int32_t *n, const char *buff, size_t buff_len, const char **end_buf, int base);
-extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int base);
-extern int git__strntol64(int64_t *n, const char *buff, size_t buff_len, const char **end_buf, int base);
-
-
-extern void git__hexdump(const char *buffer, size_t n);
-extern uint32_t git__hash(const void *key, int len, uint32_t seed);
-
-/* 32-bit cross-platform rotl */
-#ifdef _MSC_VER /* use built-in method in MSVC */
-# define git__rotl(v, s) (uint32_t)_rotl(v, s)
-#else /* use bitops in GCC; with o2 this gets optimized to a rotl instruction */
-# define git__rotl(v, s) (uint32_t)(((uint32_t)(v) << (s)) | ((uint32_t)(v) >> (32 - (s))))
-#endif
-
-extern char *git__strtok(char **end, const char *sep);
-extern char *git__strsep(char **end, const char *sep);
-
-extern void git__strntolower(char *str, size_t len);
-extern void git__strtolower(char *str);
-
-#ifdef GIT_WIN32
-GIT_INLINE(int) git__tolower(int c)
-{
- return (c >= 'A' && c <= 'Z') ? (c + 32) : c;
-}
-#else
-# define git__tolower(a) tolower(a)
-#endif
-
-extern size_t git__linenlen(const char *buffer, size_t buffer_len);
-
-GIT_INLINE(const char *) git__next_line(const char *s)
-{
- while (*s && *s != '\n') s++;
- while (*s == '\n' || *s == '\r') s++;
- return s;
-}
-
-GIT_INLINE(const void *) git__memrchr(const void *s, int c, size_t n)
-{
- const unsigned char *cp;
-
- if (n != 0) {
- cp = (unsigned char *)s + n;
- do {
- if (*(--cp) == (unsigned char)c)
- return cp;
- } while (--n != 0);
- }
-
- return NULL;
-}
-
-typedef int (*git__tsort_cmp)(const void *a, const void *b);
-
-extern void git__tsort(void **dst, size_t size, git__tsort_cmp cmp);
-
-typedef int (*git__sort_r_cmp)(const void *a, const void *b, void *payload);
-
-extern void git__tsort_r(
- void **dst, size_t size, git__sort_r_cmp cmp, void *payload);
-
-extern void git__qsort_r(
- void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload);
-
-extern void git__insertsort_r(
- void *els, size_t nel, size_t elsize, void *swapel,
- git__sort_r_cmp cmp, void *payload);
-
-/**
- * @param position If non-NULL, this will be set to the position where the
- * element is or would be inserted if not found.
- * @return 0 if found; GIT_ENOTFOUND if not found
- */
-extern int git__bsearch(
- void **array,
- size_t array_len,
- const void *key,
- int (*compare)(const void *key, const void *element),
- size_t *position);
-
-extern int git__bsearch_r(
- void **array,
- size_t array_len,
- const void *key,
- int (*compare_r)(const void *key, const void *element, void *payload),
- void *payload,
- size_t *position);
-
-extern int git__strcmp_cb(const void *a, const void *b);
-extern int git__strcasecmp_cb(const void *a, const void *b);
-
-extern int git__strcmp(const char *a, const char *b);
-extern int git__strcasecmp(const char *a, const char *b);
-extern int git__strncmp(const char *a, const char *b, size_t sz);
-extern int git__strncasecmp(const char *a, const char *b, size_t sz);
-
-extern int git__strcasesort_cmp(const char *a, const char *b);
-
-#include "thread-utils.h"
-
-typedef struct {
- git_atomic refcount;
- void *owner;
-} git_refcount;
-
-typedef void (*git_refcount_freeptr)(void *r);
-
-#define GIT_REFCOUNT_INC(r) { \
- git_atomic_inc(&((git_refcount *)(r))->refcount); \
-}
-
-#define GIT_REFCOUNT_DEC(_r, do_free) { \
- git_refcount *r = (git_refcount *)(_r); \
- int val = git_atomic_dec(&r->refcount); \
- if (val <= 0 && r->owner == NULL) { do_free(_r); } \
-}
-
-#define GIT_REFCOUNT_OWN(r, o) { \
- ((git_refcount *)(r))->owner = o; \
-}
-
-#define GIT_REFCOUNT_OWNER(r) (((git_refcount *)(r))->owner)
-
-#define GIT_REFCOUNT_VAL(r) git_atomic_get(&((git_refcount *)(r))->refcount)
-
-
-static signed char from_hex[] = {
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20 */
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 30 */
--1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 40 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 50 */
--1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 60 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a0 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* b0 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* c0 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* d0 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* e0 */
--1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* f0 */
-};
-
-GIT_INLINE(int) git__fromhex(char h)
-{
- return from_hex[(unsigned char) h];
-}
-
-GIT_INLINE(int) git__ishex(const char *str)
-{
- unsigned i;
- for (i=0; str[i] != '\0'; i++)
- if (git__fromhex(str[i]) < 0)
- return 0;
- return 1;
-}
-
-GIT_INLINE(size_t) git__size_t_bitmask(size_t v)
-{
- v--;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
-
- return v;
-}
-
-GIT_INLINE(size_t) git__size_t_powerof2(size_t v)
-{
- return git__size_t_bitmask(v) + 1;
-}
-
-GIT_INLINE(bool) git__isupper(int c)
-{
- return (c >= 'A' && c <= 'Z');
-}
-
-GIT_INLINE(bool) git__isalpha(int c)
-{
- return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
-}
-
-GIT_INLINE(bool) git__isdigit(int c)
-{
- return (c >= '0' && c <= '9');
-}
-
-GIT_INLINE(bool) git__isspace(int c)
-{
- return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
-}
-
-GIT_INLINE(bool) git__isspace_nonlf(int c)
-{
- return (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\v');
-}
-
-GIT_INLINE(bool) git__iswildcard(int c)
-{
- return (c == '*' || c == '?' || c == '[');
-}
-
-GIT_INLINE(bool) git__isxdigit(int c)
-{
- return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
-/*
- * Parse a string value as a boolean, just like Core Git does.
- *
- * Valid values for true are: 'true', 'yes', 'on'
- * Valid values for false are: 'false', 'no', 'off'
- */
-extern int git__parse_bool(int *out, const char *value);
-
-/*
- * Parse a string into a value as a git_time_t.
- *
- * Sample valid input:
- * - "yesterday"
- * - "July 17, 2003"
- * - "2003-7-17 08:23"
- */
-extern int git__date_parse(git_time_t *out, const char *date);
-
-/*
- * Format a git_time as a RFC2822 string
- *
- * @param out buffer to store formatted date; a '\\0' terminator will automatically be added.
- * @param len size of the buffer; should be atleast `GIT_DATE_RFC2822_SZ` in size;
- * @param date the date to be formatted
- * @return 0 if successful; -1 on error
- */
-extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date);
-
-/*
- * Unescapes a string in-place.
- *
- * Edge cases behavior:
- * - "jackie\" -> "jacky\"
- * - "chan\\" -> "chan\"
- */
-extern size_t git__unescape(char *str);
-
-/*
- * Iterate through an UTF-8 string, yielding one
- * codepoint at a time.
- *
- * @param str current position in the string
- * @param str_len size left in the string; -1 if the string is NULL-terminated
- * @param dst pointer where to store the current codepoint
- * @return length in bytes of the read codepoint; -1 if the codepoint was invalid
- */
-extern int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst);
-
-/*
- * Safely zero-out memory, making sure that the compiler
- * doesn't optimize away the operation.
- */
-GIT_INLINE(void) git__memzero(void *data, size_t size)
-{
-#ifdef _MSC_VER
- SecureZeroMemory((PVOID)data, size);
-#else
- volatile uint8_t *scan = (volatile uint8_t *)data;
-
- while (size--)
- *scan++ = 0x0;
-#endif
-}
-
-#ifdef GIT_WIN32
-
-GIT_INLINE(double) git__timer(void)
-{
- /* We need the initial tick count to detect if the tick
- * count has rolled over. */
- static DWORD initial_tick_count = 0;
-
- /* GetTickCount returns the number of milliseconds that have
- * elapsed since the system was started. */
- DWORD count = GetTickCount();
-
- if(initial_tick_count == 0) {
- initial_tick_count = count;
- } else if (count < initial_tick_count) {
- /* The tick count has rolled over - adjust for it. */
- count = (0xFFFFFFFF - initial_tick_count) + count;
- }
-
- return (double) count / (double) 1000;
-}
-
-#elif __APPLE__
-
-#include <mach/mach_time.h>
-
-GIT_INLINE(double) git__timer(void)
-{
- uint64_t time = mach_absolute_time();
- static double scaling_factor = 0;
-
- if (scaling_factor == 0) {
- mach_timebase_info_data_t info;
- (void)mach_timebase_info(&info);
- scaling_factor = (double)info.numer / (double)info.denom;
- }
-
- return (double)time * scaling_factor / 1.0E9;
-}
-
-#elif defined(AMIGA)
-
-#include <proto/timer.h>
-
-GIT_INLINE(double) git__timer(void)
-{
- struct TimeVal tv;
- ITimer->GetUpTime(&tv);
- return (double)tv.Seconds + (double)tv.Microseconds / 1.0E6;
-}
-
-#else
-
-#include <sys/time.h>
-
-GIT_INLINE(double) git__timer(void)
-{
- struct timespec tp;
-
- if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
- return (double) tp.tv_sec + (double) tp.tv_nsec / 1.0E9;
- } else {
- /* Fall back to using gettimeofday */
- struct timeval tv;
- struct timezone tz;
- gettimeofday(&tv, &tz);
- return (double)tv.tv_sec + (double)tv.tv_usec / 1.0E6;
- }
-}
-
-#endif
-
-extern int git__getenv(git_buf *out, const char *name);
-
-#endif /* INCLUDE_util_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "varint.h"
-
-uintmax_t git_decode_varint(const unsigned char *bufp, size_t *varint_len)
-{
- const unsigned char *buf = bufp;
- unsigned char c = *buf++;
- uintmax_t val = c & 127;
- while (c & 128) {
- val += 1;
- if (!val || MSB(val, 7)) {
- /* This is not a valid varint_len, so it signals
- the error */
- *varint_len = 0;
- return 0; /* overflow */
- }
- c = *buf++;
- val = (val << 7) + (c & 127);
- }
- *varint_len = buf - bufp;
- return val;
-}
-
-int git_encode_varint(unsigned char *buf, size_t bufsize, uintmax_t value)
-{
- unsigned char varint[16];
- unsigned pos = sizeof(varint) - 1;
- varint[pos] = value & 127;
- while (value >>= 7)
- varint[--pos] = 128 | (--value & 127);
- if (buf) {
- if (bufsize < pos)
- return -1;
- memcpy(buf, varint + pos, sizeof(varint) - pos);
- }
- return sizeof(varint) - pos;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_varint_h__
-#define INCLUDE_varint_h__
-
-#include <stdint.h>
-
-extern int git_encode_varint(unsigned char *, size_t, uintmax_t);
-extern uintmax_t git_decode_varint(const unsigned char *, size_t *);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "vector.h"
-#include "integer.h"
-
-/* In elements, not bytes */
-#define MIN_ALLOCSIZE 8
-
-GIT_INLINE(size_t) compute_new_size(git_vector *v)
-{
- size_t new_size = v->_alloc_size;
-
- /* Use a resize factor of 1.5, which is quick to compute using integer
- * instructions and less than the golden ratio (1.618...) */
- if (new_size < MIN_ALLOCSIZE)
- new_size = MIN_ALLOCSIZE;
- else if (new_size <= (SIZE_MAX / 3) * 2)
- new_size += new_size / 2;
- else
- new_size = SIZE_MAX;
-
- return new_size;
-}
-
-GIT_INLINE(int) resize_vector(git_vector *v, size_t new_size)
-{
- void *new_contents;
-
- new_contents = git__reallocarray(v->contents, new_size, sizeof(void *));
- GITERR_CHECK_ALLOC(new_contents);
-
- v->_alloc_size = new_size;
- v->contents = new_contents;
-
- return 0;
-}
-
-int git_vector_size_hint(git_vector *v, size_t size_hint)
-{
- if (v->_alloc_size >= size_hint)
- return 0;
- return resize_vector(v, size_hint);
-}
-
-int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
-{
- size_t bytes;
-
- assert(v && src);
-
- GITERR_CHECK_ALLOC_MULTIPLY(&bytes, src->length, sizeof(void *));
-
- v->_alloc_size = src->length;
- v->_cmp = cmp ? cmp : src->_cmp;
- v->length = src->length;
- v->flags = src->flags;
- if (cmp != src->_cmp)
- git_vector_set_sorted(v, 0);
- v->contents = git__malloc(bytes);
- GITERR_CHECK_ALLOC(v->contents);
-
- memcpy(v->contents, src->contents, bytes);
-
- return 0;
-}
-
-void git_vector_free(git_vector *v)
-{
- assert(v);
-
- git__free(v->contents);
- v->contents = NULL;
-
- v->length = 0;
- v->_alloc_size = 0;
-}
-
-void git_vector_free_deep(git_vector *v)
-{
- size_t i;
-
- assert(v);
-
- for (i = 0; i < v->length; ++i) {
- git__free(v->contents[i]);
- v->contents[i] = NULL;
- }
-
- git_vector_free(v);
-}
-
-int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp)
-{
- assert(v);
-
- v->_alloc_size = 0;
- v->_cmp = cmp;
- v->length = 0;
- v->flags = GIT_VECTOR_SORTED;
- v->contents = NULL;
-
- return resize_vector(v, max(initial_size, MIN_ALLOCSIZE));
-}
-
-void **git_vector_detach(size_t *size, size_t *asize, git_vector *v)
-{
- void **data = v->contents;
-
- if (size)
- *size = v->length;
- if (asize)
- *asize = v->_alloc_size;
-
- v->_alloc_size = 0;
- v->length = 0;
- v->contents = NULL;
-
- return data;
-}
-
-int git_vector_insert(git_vector *v, void *element)
-{
- assert(v);
-
- if (v->length >= v->_alloc_size &&
- resize_vector(v, compute_new_size(v)) < 0)
- return -1;
-
- v->contents[v->length++] = element;
-
- git_vector_set_sorted(v, v->length <= 1);
-
- return 0;
-}
-
-int git_vector_insert_sorted(
- git_vector *v, void *element, int (*on_dup)(void **old, void *new))
-{
- int result;
- size_t pos;
-
- assert(v && v->_cmp);
-
- if (!git_vector_is_sorted(v))
- git_vector_sort(v);
-
- if (v->length >= v->_alloc_size &&
- resize_vector(v, compute_new_size(v)) < 0)
- return -1;
-
- /* If we find the element and have a duplicate handler callback,
- * invoke it. If it returns non-zero, then cancel insert, otherwise
- * proceed with normal insert.
- */
- if (!git__bsearch(v->contents, v->length, element, v->_cmp, &pos) &&
- on_dup && (result = on_dup(&v->contents[pos], element)) < 0)
- return result;
-
- /* shift elements to the right */
- if (pos < v->length)
- memmove(v->contents + pos + 1, v->contents + pos,
- (v->length - pos) * sizeof(void *));
-
- v->contents[pos] = element;
- v->length++;
-
- return 0;
-}
-
-void git_vector_sort(git_vector *v)
-{
- assert(v);
-
- if (git_vector_is_sorted(v) || !v->_cmp)
- return;
-
- if (v->length > 1)
- git__tsort(v->contents, v->length, v->_cmp);
- git_vector_set_sorted(v, 1);
-}
-
-int git_vector_bsearch2(
- size_t *at_pos,
- git_vector *v,
- git_vector_cmp key_lookup,
- const void *key)
-{
- assert(v && key && key_lookup);
-
- /* need comparison function to sort the vector */
- if (!v->_cmp)
- return -1;
-
- git_vector_sort(v);
-
- return git__bsearch(v->contents, v->length, key, key_lookup, at_pos);
-}
-
-int git_vector_search2(
- size_t *at_pos, const git_vector *v, git_vector_cmp key_lookup, const void *key)
-{
- size_t i;
-
- assert(v && key && key_lookup);
-
- for (i = 0; i < v->length; ++i) {
- if (key_lookup(key, v->contents[i]) == 0) {
- if (at_pos)
- *at_pos = i;
-
- return 0;
- }
- }
-
- return GIT_ENOTFOUND;
-}
-
-static int strict_comparison(const void *a, const void *b)
-{
- return (a == b) ? 0 : -1;
-}
-
-int git_vector_search(size_t *at_pos, const git_vector *v, const void *entry)
-{
- return git_vector_search2(at_pos, v, v->_cmp ? v->_cmp : strict_comparison, entry);
-}
-
-int git_vector_remove(git_vector *v, size_t idx)
-{
- size_t shift_count;
-
- assert(v);
-
- if (idx >= v->length)
- return GIT_ENOTFOUND;
-
- shift_count = v->length - idx - 1;
-
- if (shift_count)
- memmove(&v->contents[idx], &v->contents[idx + 1],
- shift_count * sizeof(void *));
-
- v->length--;
- return 0;
-}
-
-void git_vector_pop(git_vector *v)
-{
- if (v->length > 0)
- v->length--;
-}
-
-void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *))
-{
- git_vector_cmp cmp;
- size_t i, j;
-
- if (v->length <= 1)
- return;
-
- git_vector_sort(v);
- cmp = v->_cmp ? v->_cmp : strict_comparison;
-
- for (i = 0, j = 1 ; j < v->length; ++j)
- if (!cmp(v->contents[i], v->contents[j])) {
- if (git_free_cb)
- git_free_cb(v->contents[i]);
-
- v->contents[i] = v->contents[j];
- } else
- v->contents[++i] = v->contents[j];
-
- v->length -= j - i - 1;
-}
-
-void git_vector_remove_matching(
- git_vector *v,
- int (*match)(const git_vector *v, size_t idx, void *payload),
- void *payload)
-{
- size_t i, j;
-
- for (i = 0, j = 0; j < v->length; ++j) {
- v->contents[i] = v->contents[j];
-
- if (!match(v, i, payload))
- i++;
- }
-
- v->length = i;
-}
-
-void git_vector_clear(git_vector *v)
-{
- assert(v);
- v->length = 0;
- git_vector_set_sorted(v, 1);
-}
-
-void git_vector_swap(git_vector *a, git_vector *b)
-{
- git_vector t;
-
- assert(a && b);
-
- if (a != b) {
- memcpy(&t, a, sizeof(t));
- memcpy(a, b, sizeof(t));
- memcpy(b, &t, sizeof(t));
- }
-}
-
-int git_vector_resize_to(git_vector *v, size_t new_length)
-{
- if (new_length > v->_alloc_size &&
- resize_vector(v, new_length) < 0)
- return -1;
-
- if (new_length > v->length)
- memset(&v->contents[v->length], 0,
- sizeof(void *) * (new_length - v->length));
-
- v->length = new_length;
-
- return 0;
-}
-
-int git_vector_insert_null(git_vector *v, size_t idx, size_t insert_len)
-{
- size_t new_length;
-
- assert(insert_len > 0 && idx <= v->length);
-
- GITERR_CHECK_ALLOC_ADD(&new_length, v->length, insert_len);
-
- if (new_length > v->_alloc_size && resize_vector(v, new_length) < 0)
- return -1;
-
- memmove(&v->contents[idx + insert_len], &v->contents[idx],
- sizeof(void *) * (v->length - idx));
- memset(&v->contents[idx], 0, sizeof(void *) * insert_len);
-
- v->length = new_length;
- return 0;
-}
-
-int git_vector_remove_range(git_vector *v, size_t idx, size_t remove_len)
-{
- size_t new_length = v->length - remove_len;
- size_t end_idx = 0;
-
- assert(remove_len > 0);
-
- if (git__add_sizet_overflow(&end_idx, idx, remove_len))
- assert(0);
-
- assert(end_idx <= v->length);
-
- if (end_idx < v->length)
- memmove(&v->contents[idx], &v->contents[end_idx],
- sizeof(void *) * (v->length - end_idx));
-
- memset(&v->contents[new_length], 0, sizeof(void *) * remove_len);
-
- v->length = new_length;
- return 0;
-}
-
-int git_vector_set(void **old, git_vector *v, size_t position, void *value)
-{
- if (position + 1 > v->length) {
- if (git_vector_resize_to(v, position + 1) < 0)
- return -1;
- }
-
- if (old != NULL)
- *old = v->contents[position];
-
- v->contents[position] = value;
-
- return 0;
-}
-
-int git_vector_verify_sorted(const git_vector *v)
-{
- size_t i;
-
- if (!git_vector_is_sorted(v))
- return -1;
-
- for (i = 1; i < v->length; ++i) {
- if (v->_cmp(v->contents[i - 1], v->contents[i]) > 0)
- return -1;
- }
-
- return 0;
-}
-
-void git_vector_reverse(git_vector *v)
-{
- size_t a, b;
-
- a = 0;
- b = v->length - 1;
-
- while (a < b) {
- void *tmp = v->contents[a];
- v->contents[a] = v->contents[b];
- v->contents[b] = tmp;
- a++;
- b--;
- }
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_vector_h__
-#define INCLUDE_vector_h__
-
-#include "common.h"
-
-typedef int (*git_vector_cmp)(const void *, const void *);
-
-enum {
- GIT_VECTOR_SORTED = (1u << 0),
- GIT_VECTOR_FLAG_MAX = (1u << 1),
-};
-
-typedef struct git_vector {
- size_t _alloc_size;
- git_vector_cmp _cmp;
- void **contents;
- size_t length;
- uint32_t flags;
-} git_vector;
-
-#define GIT_VECTOR_INIT {0}
-
-int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp);
-void git_vector_free(git_vector *v);
-void git_vector_free_deep(git_vector *v); /* free each entry and self */
-void git_vector_clear(git_vector *v);
-int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp);
-void git_vector_swap(git_vector *a, git_vector *b);
-int git_vector_size_hint(git_vector *v, size_t size_hint);
-
-void **git_vector_detach(size_t *size, size_t *asize, git_vector *v);
-
-void git_vector_sort(git_vector *v);
-
-/** Linear search for matching entry using internal comparison function */
-int git_vector_search(size_t *at_pos, const git_vector *v, const void *entry);
-
-/** Linear search for matching entry using explicit comparison function */
-int git_vector_search2(size_t *at_pos, const git_vector *v, git_vector_cmp cmp, const void *key);
-
-/**
- * Binary search for matching entry using explicit comparison function that
- * returns position where item would go if not found.
- */
-int git_vector_bsearch2(
- size_t *at_pos, git_vector *v, git_vector_cmp cmp, const void *key);
-
-/** Binary search for matching entry using internal comparison function */
-GIT_INLINE(int) git_vector_bsearch(size_t *at_pos, git_vector *v, const void *key)
-{
- return git_vector_bsearch2(at_pos, v, v->_cmp, key);
-}
-
-GIT_INLINE(void *) git_vector_get(const git_vector *v, size_t position)
-{
- return (position < v->length) ? v->contents[position] : NULL;
-}
-
-#define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL)
-
-GIT_INLINE(size_t) git_vector_length(const git_vector *v)
-{
- return v->length;
-}
-
-GIT_INLINE(void *) git_vector_last(const git_vector *v)
-{
- return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
-}
-
-#define git_vector_foreach(v, iter, elem) \
- for ((iter) = 0; (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ )
-
-#define git_vector_rforeach(v, iter, elem) \
- for ((iter) = (v)->length - 1; (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- )
-
-int git_vector_insert(git_vector *v, void *element);
-int git_vector_insert_sorted(git_vector *v, void *element,
- int (*on_dup)(void **old, void *new));
-int git_vector_remove(git_vector *v, size_t idx);
-void git_vector_pop(git_vector *v);
-void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *));
-
-void git_vector_remove_matching(
- git_vector *v,
- int (*match)(const git_vector *v, size_t idx, void *payload),
- void *payload);
-
-int git_vector_resize_to(git_vector *v, size_t new_length);
-int git_vector_insert_null(git_vector *v, size_t idx, size_t insert_len);
-int git_vector_remove_range(git_vector *v, size_t idx, size_t remove_len);
-
-int git_vector_set(void **old, git_vector *v, size_t position, void *value);
-
-/** Check if vector is sorted */
-#define git_vector_is_sorted(V) (((V)->flags & GIT_VECTOR_SORTED) != 0)
-
-/** Directly set sorted state of vector */
-#define git_vector_set_sorted(V,S) do { \
- (V)->flags = (S) ? ((V)->flags | GIT_VECTOR_SORTED) : \
- ((V)->flags & ~GIT_VECTOR_SORTED); } while (0)
-
-/** Set the comparison function used for sorting the vector */
-GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp)
-{
- if (cmp != v->_cmp) {
- v->_cmp = cmp;
- git_vector_set_sorted(v, 0);
- }
-}
-
-/* Just use this in tests, not for realz. returns -1 if not sorted */
-int git_vector_verify_sorted(const git_vector *v);
-
-/**
- * Reverse the vector in-place.
- */
-void git_vector_reverse(git_vector *v);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#define GIT__WIN32_NO_WRAP_DIR
-#include "posix.h"
-
-git__DIR *git__opendir(const char *dir)
-{
- git_win32_path filter_w;
- git__DIR *new = NULL;
- size_t dirlen, alloclen;
-
- if (!dir || !git_win32__findfirstfile_filter(filter_w, dir))
- return NULL;
-
- dirlen = strlen(dir);
-
- if (GIT_ADD_SIZET_OVERFLOW(&alloclen, sizeof(*new), dirlen) ||
- GIT_ADD_SIZET_OVERFLOW(&alloclen, alloclen, 1) ||
- !(new = git__calloc(1, alloclen)))
- return NULL;
-
- memcpy(new->dir, dir, dirlen);
-
- new->h = FindFirstFileW(filter_w, &new->f);
-
- if (new->h == INVALID_HANDLE_VALUE) {
- giterr_set(GITERR_OS, "Could not open directory '%s'", dir);
- git__free(new);
- return NULL;
- }
-
- new->first = 1;
- return new;
-}
-
-int git__readdir_ext(
- git__DIR *d,
- struct git__dirent *entry,
- struct git__dirent **result,
- int *is_dir)
-{
- if (!d || !entry || !result || d->h == INVALID_HANDLE_VALUE)
- return -1;
-
- *result = NULL;
-
- if (d->first)
- d->first = 0;
- else if (!FindNextFileW(d->h, &d->f)) {
- if (GetLastError() == ERROR_NO_MORE_FILES)
- return 0;
- giterr_set(GITERR_OS, "Could not read from directory '%s'", d->dir);
- return -1;
- }
-
- /* Convert the path to UTF-8 */
- if (git_win32_path_to_utf8(entry->d_name, d->f.cFileName) < 0)
- return -1;
-
- entry->d_ino = 0;
-
- *result = entry;
-
- if (is_dir != NULL)
- *is_dir = ((d->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
-
- return 0;
-}
-
-struct git__dirent *git__readdir(git__DIR *d)
-{
- struct git__dirent *result;
- if (git__readdir_ext(d, &d->entry, &result, NULL) < 0)
- return NULL;
- return result;
-}
-
-void git__rewinddir(git__DIR *d)
-{
- git_win32_path filter_w;
-
- if (!d)
- return;
-
- if (d->h != INVALID_HANDLE_VALUE) {
- FindClose(d->h);
- d->h = INVALID_HANDLE_VALUE;
- d->first = 0;
- }
-
- if (!git_win32__findfirstfile_filter(filter_w, d->dir))
- return;
-
- d->h = FindFirstFileW(filter_w, &d->f);
-
- if (d->h == INVALID_HANDLE_VALUE)
- giterr_set(GITERR_OS, "Could not open directory '%s'", d->dir);
- else
- d->first = 1;
-}
-
-int git__closedir(git__DIR *d)
-{
- if (!d)
- return 0;
-
- if (d->h != INVALID_HANDLE_VALUE) {
- FindClose(d->h);
- d->h = INVALID_HANDLE_VALUE;
- }
-
- git__free(d);
- return 0;
-}
-
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_dir_h__
-#define INCLUDE_dir_h__
-
-#include "common.h"
-#include "w32_util.h"
-
-struct git__dirent {
- int d_ino;
- git_win32_utf8_path d_name;
-};
-
-typedef struct {
- HANDLE h;
- WIN32_FIND_DATAW f;
- struct git__dirent entry;
- int first;
- char dir[GIT_FLEX_ARRAY];
-} git__DIR;
-
-extern git__DIR *git__opendir(const char *);
-extern struct git__dirent *git__readdir(git__DIR *);
-extern int git__readdir_ext(
- git__DIR *, struct git__dirent *, struct git__dirent **, int *);
-extern void git__rewinddir(git__DIR *);
-extern int git__closedir(git__DIR *);
-
-# ifndef GIT__WIN32_NO_WRAP_DIR
-# define dirent git__dirent
-# define DIR git__DIR
-# define opendir git__opendir
-# define readdir git__readdir
-# define readdir_r(d,e,r) git__readdir_ext((d),(e),(r),NULL)
-# define rewinddir git__rewinddir
-# define closedir git__closedir
-# endif
-
-#endif /* INCLUDE_dir_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "error.h"
-#include "utf-conv.h"
-
-#ifdef GIT_WINHTTP
-# include <winhttp.h>
-#endif
-
-char *git_win32_get_error_message(DWORD error_code)
-{
- LPWSTR lpMsgBuf = NULL;
- HMODULE hModule = NULL;
- char *utf8_msg = NULL;
- DWORD dwFlags =
- FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS;
-
- if (!error_code)
- return NULL;
-
-#ifdef GIT_WINHTTP
- /* Errors raised by WinHTTP are not in the system resource table */
- if (error_code >= WINHTTP_ERROR_BASE &&
- error_code <= WINHTTP_ERROR_LAST)
- hModule = GetModuleHandleW(L"winhttp");
-#endif
-
- GIT_UNUSED(hModule);
-
- if (hModule)
- dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
- else
- dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
-
- if (FormatMessageW(dwFlags, hModule, error_code,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPWSTR)&lpMsgBuf, 0, NULL)) {
- /* Convert the message to UTF-8. If this fails, we will
- * return NULL, which is a condition expected by the caller */
- if (git__utf16_to_8_alloc(&utf8_msg, lpMsgBuf) < 0)
- utf8_msg = NULL;
-
- LocalFree(lpMsgBuf);
- }
-
- return utf8_msg;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_git_win32_error_h__
-#define INCLUDE_git_win32_error_h__
-
-extern char *git_win32_get_error_message(DWORD error_code);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "path_w32.h"
-#include "utf-conv.h"
-#include "path.h"
-#include "findfile.h"
-
-#define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
-
-#ifndef _WIN64
-#define REG_MSYSGIT_INSTALL REG_MSYSGIT_INSTALL_LOCAL
-#else
-#define REG_MSYSGIT_INSTALL L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
-#endif
-
-typedef struct {
- git_win32_path path;
- DWORD len;
-} _findfile_path;
-
-static int git_win32__expand_path(_findfile_path *dest, const wchar_t *src)
-{
- dest->len = ExpandEnvironmentStringsW(src, dest->path, ARRAY_SIZE(dest->path));
-
- if (!dest->len || dest->len > ARRAY_SIZE(dest->path))
- return -1;
-
- return 0;
-}
-
-static int win32_path_to_8(git_buf *dest, const wchar_t *src)
-{
- git_win32_utf8_path utf8_path;
-
- if (git_win32_path_to_utf8(utf8_path, src) < 0) {
- giterr_set(GITERR_OS, "Unable to convert path to UTF-8");
- return -1;
- }
-
- /* Convert backslashes to forward slashes */
- git_path_mkposix(utf8_path);
-
- return git_buf_sets(dest, utf8_path);
-}
-
-static wchar_t* win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen)
-{
- wchar_t term, *base = path;
-
- assert(path && buf && buflen);
-
- term = (*path == L'"') ? *path++ : L';';
-
- for (buflen--; *path && *path != term && buflen; buflen--)
- *buf++ = *path++;
-
- *buf = L'\0'; /* reserved a byte via initial subtract */
-
- while (*path == term || *path == L';')
- path++;
-
- return (path != base) ? path : NULL;
-}
-
-static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe, const wchar_t *subdir)
-{
- wchar_t *env = _wgetenv(L"PATH"), lastch;
- _findfile_path root;
- size_t gitexe_len = wcslen(gitexe);
-
- if (!env)
- return -1;
-
- while ((env = win32_walkpath(env, root.path, MAX_PATH-1)) && *root.path) {
- root.len = (DWORD)wcslen(root.path);
- lastch = root.path[root.len - 1];
-
- /* ensure trailing slash (MAX_PATH-1 to walkpath guarantees space) */
- if (lastch != L'/' && lastch != L'\\') {
- root.path[root.len++] = L'\\';
- root.path[root.len] = L'\0';
- }
-
- if (root.len + gitexe_len >= MAX_PATH)
- continue;
- wcscpy(&root.path[root.len], gitexe);
-
- if (_waccess(root.path, F_OK) == 0 && root.len > 5) {
- /* replace "bin\\" or "cmd\\" with subdir */
- wcscpy(&root.path[root.len - 4], subdir);
-
- win32_path_to_8(buf, root.path);
- return 0;
- }
- }
-
- return GIT_ENOTFOUND;
-}
-
-static int win32_find_git_in_registry(
- git_buf *buf, const HKEY hive, const wchar_t *key, const wchar_t *subdir)
-{
- HKEY hKey;
- int error = GIT_ENOTFOUND;
-
- assert(buf);
-
- if (!RegOpenKeyExW(hive, key, 0, KEY_READ, &hKey)) {
- DWORD dwType, cbData;
- git_win32_path path;
-
- /* Ensure that the buffer is big enough to have the suffix attached
- * after we receive the result. */
- cbData = (DWORD)(sizeof(path) - wcslen(subdir) * sizeof(wchar_t));
-
- /* InstallLocation points to the root of the git directory */
- if (!RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType, (LPBYTE)path, &cbData) &&
- dwType == REG_SZ) {
-
- /* Append the suffix */
- wcscat(path, subdir);
-
- /* Convert to UTF-8, with forward slashes, and output the path
- * to the provided buffer */
- if (!win32_path_to_8(buf, path))
- error = 0;
- }
-
- RegCloseKey(hKey);
- }
-
- return error;
-}
-
-static int win32_find_existing_dirs(
- git_buf *out, const wchar_t *tmpl[])
-{
- _findfile_path path16;
- git_buf buf = GIT_BUF_INIT;
-
- git_buf_clear(out);
-
- for (; *tmpl != NULL; tmpl++) {
- if (!git_win32__expand_path(&path16, *tmpl) &&
- path16.path[0] != L'%' &&
- !_waccess(path16.path, F_OK))
- {
- win32_path_to_8(&buf, path16.path);
-
- if (buf.size)
- git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
- }
- }
-
- git_buf_free(&buf);
-
- return (git_buf_oom(out) ? -1 : 0);
-}
-
-int git_win32__find_system_dirs(git_buf *out, const wchar_t *subdir)
-{
- git_buf buf = GIT_BUF_INIT;
-
- /* directories where git.exe & git.cmd are found */
- if (!win32_find_git_in_path(&buf, L"git.exe", subdir) && buf.size)
- git_buf_set(out, buf.ptr, buf.size);
- else
- git_buf_clear(out);
-
- if (!win32_find_git_in_path(&buf, L"git.cmd", subdir) && buf.size)
- git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
-
- /* directories where git is installed according to registry */
- if (!win32_find_git_in_registry(
- &buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size)
- git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
-
- if (!win32_find_git_in_registry(
- &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL, subdir) && buf.size)
- git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
-
- git_buf_free(&buf);
-
- return (git_buf_oom(out) ? -1 : 0);
-}
-
-int git_win32__find_global_dirs(git_buf *out)
-{
- static const wchar_t *global_tmpls[4] = {
- L"%HOME%\\",
- L"%HOMEDRIVE%%HOMEPATH%\\",
- L"%USERPROFILE%\\",
- NULL,
- };
-
- return win32_find_existing_dirs(out, global_tmpls);
-}
-
-int git_win32__find_xdg_dirs(git_buf *out)
-{
- static const wchar_t *global_tmpls[7] = {
- L"%XDG_CONFIG_HOME%\\git",
- L"%APPDATA%\\git",
- L"%LOCALAPPDATA%\\git",
- L"%HOME%\\.config\\git",
- L"%HOMEDRIVE%%HOMEPATH%\\.config\\git",
- L"%USERPROFILE%\\.config\\git",
- NULL,
- };
-
- return win32_find_existing_dirs(out, global_tmpls);
-}
-
-int git_win32__find_programdata_dirs(git_buf *out)
-{
- static const wchar_t *programdata_tmpls[2] = {
- L"%PROGRAMDATA%\\Git",
- NULL,
- };
-
- return win32_find_existing_dirs(out, programdata_tmpls);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_git_findfile_h__
-#define INCLUDE_git_findfile_h__
-
-extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath);
-extern int git_win32__find_global_dirs(git_buf *out);
-extern int git_win32__find_xdg_dirs(git_buf *out);
-extern int git_win32__find_programdata_dirs(git_buf *out);
-
-#endif
-
+++ /dev/null
-#include <winver.h>
-#include "../../include/git2/version.h"
-
-#ifndef LIBGIT2_FILENAME
-# define LIBGIT2_FILENAME "git2"
-#endif
-
-#ifndef LIBGIT2_COMMENTS
-# define LIBGIT2_COMMENTS "For more information visit http://libgit2.github.com/"
-#endif
-
-VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE
- FILEVERSION LIBGIT2_VER_MAJOR,LIBGIT2_VER_MINOR,LIBGIT2_VER_REVISION,LIBGIT2_VER_PATCH
- PRODUCTVERSION LIBGIT2_VER_MAJOR,LIBGIT2_VER_MINOR,LIBGIT2_VER_REVISION,LIBGIT2_VER_PATCH
- FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
-#ifdef _DEBUG
- FILEFLAGS VS_FF_DEBUG
-#else
- FILEFLAGS 0
-#endif
- FILEOS VOS_NT_WINDOWS32
- FILETYPE VFT_DLL
- FILESUBTYPE VFT2_UNKNOWN
-BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "040904E4"
- //language ID = U.S. English, char set = Windows, Multilingual
- BEGIN
- VALUE "FileDescription", "libgit2 - the Git linkable library\0"
- VALUE "FileVersion", LIBGIT2_VERSION "\0"
- VALUE "InternalName", LIBGIT2_FILENAME ".dll\0"
- VALUE "LegalCopyright", "Copyright (C) the libgit2 contributors. All rights reserved.\0"
- VALUE "OriginalFilename", LIBGIT2_FILENAME ".dll\0"
- VALUE "ProductName", "libgit2\0"
- VALUE "ProductVersion", LIBGIT2_VERSION "\0"
- VALUE "Comments", LIBGIT2_COMMENTS "\0"
- END
- END
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x0409, 1252
- END
-END
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "map.h"
-#include <errno.h>
-
-#ifndef NO_MMAP
-
-static DWORD get_page_size(void)
-{
- static DWORD page_size;
- SYSTEM_INFO sys;
-
- if (!page_size) {
- GetSystemInfo(&sys);
- page_size = sys.dwPageSize;
- }
-
- return page_size;
-}
-
-static DWORD get_allocation_granularity(void)
-{
- static DWORD granularity;
- SYSTEM_INFO sys;
-
- if (!granularity) {
- GetSystemInfo(&sys);
- granularity = sys.dwAllocationGranularity;
- }
-
- return granularity;
-}
-
-int git__page_size(size_t *page_size)
-{
- *page_size = get_page_size();
- return 0;
-}
-
-int git__mmap_alignment(size_t *page_size)
-{
- *page_size = get_allocation_granularity();
- return 0;
-}
-
-int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
-{
- HANDLE fh = (HANDLE)_get_osfhandle(fd);
- DWORD alignment = get_allocation_granularity();
- DWORD fmap_prot = 0;
- DWORD view_prot = 0;
- DWORD off_low = 0;
- DWORD off_hi = 0;
- git_off_t page_start;
- git_off_t page_offset;
-
- GIT_MMAP_VALIDATE(out, len, prot, flags);
-
- out->data = NULL;
- out->len = 0;
- out->fmh = NULL;
-
- if (fh == INVALID_HANDLE_VALUE) {
- errno = EBADF;
- giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");
- return -1;
- }
-
- if (prot & GIT_PROT_WRITE)
- fmap_prot |= PAGE_READWRITE;
- else if (prot & GIT_PROT_READ)
- fmap_prot |= PAGE_READONLY;
-
- if (prot & GIT_PROT_WRITE)
- view_prot |= FILE_MAP_WRITE;
- if (prot & GIT_PROT_READ)
- view_prot |= FILE_MAP_READ;
-
- page_start = (offset / alignment) * alignment;
- page_offset = offset - page_start;
-
- if (page_offset != 0) { /* offset must be multiple of the allocation granularity */
- errno = EINVAL;
- giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of allocation granularity");
- return -1;
- }
-
- out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL);
- if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) {
- giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");
- out->fmh = NULL;
- return -1;
- }
-
- assert(sizeof(git_off_t) == 8);
-
- off_low = (DWORD)(page_start);
- off_hi = (DWORD)(page_start >> 32);
- out->data = MapViewOfFile(out->fmh, view_prot, off_hi, off_low, len);
- if (!out->data) {
- giterr_set(GITERR_OS, "Failed to mmap. No data written");
- CloseHandle(out->fmh);
- out->fmh = NULL;
- return -1;
- }
- out->len = len;
-
- return 0;
-}
-
-int p_munmap(git_map *map)
-{
- int error = 0;
-
- assert(map != NULL);
-
- if (map->data) {
- if (!UnmapViewOfFile(map->data)) {
- giterr_set(GITERR_OS, "Failed to munmap. Could not unmap view of file");
- error = -1;
- }
- map->data = NULL;
- }
-
- if (map->fmh) {
- if (!CloseHandle(map->fmh)) {
- giterr_set(GITERR_OS, "Failed to munmap. Could not close handle");
- error = -1;
- }
- map->fmh = NULL;
- }
-
- return error;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_mingw_compat__
-#define INCLUDE_mingw_compat__
-
-#if defined(__MINGW32__)
-
-#undef stat
-
-#if _WIN32_WINNT < 0x0600 && !defined(__MINGW64_VERSION_MAJOR)
-#undef MemoryBarrier
-void __mingworg_MemoryBarrier(void);
-#define MemoryBarrier __mingworg_MemoryBarrier
-#define VOLUME_NAME_DOS 0x0
-#endif
-
-#endif
-
-#endif /* INCLUDE_mingw_compat__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_msvc_compat__
-#define INCLUDE_msvc_compat__
-
-#if defined(_MSC_VER)
-
-typedef unsigned short mode_t;
-typedef SSIZE_T ssize_t;
-
-#define strcasecmp(s1, s2) _stricmp(s1, s2)
-#define strncasecmp(s1, s2, c) _strnicmp(s1, s2, c)
-
-#endif
-
-#define GIT_STDLIB_CALL __cdecl
-
-#endif /* INCLUDE_msvc_compat__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "path.h"
-#include "path_w32.h"
-#include "utf-conv.h"
-#include "posix.h"
-#include "reparse.h"
-#include "dir.h"
-
-#define PATH__NT_NAMESPACE L"\\\\?\\"
-#define PATH__NT_NAMESPACE_LEN 4
-
-#define PATH__ABSOLUTE_LEN 3
-
-#define path__is_dirsep(p) ((p) == '/' || (p) == '\\')
-
-#define path__is_absolute(p) \
- (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
-
-#define path__is_nt_namespace(p) \
- (((p)[0] == '\\' && (p)[1] == '\\' && (p)[2] == '?' && (p)[3] == '\\') || \
- ((p)[0] == '/' && (p)[1] == '/' && (p)[2] == '?' && (p)[3] == '/'))
-
-#define path__is_unc(p) \
- (((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/'))
-
-GIT_INLINE(int) path__cwd(wchar_t *path, int size)
-{
- int len;
-
- if ((len = GetCurrentDirectoryW(size, path)) == 0) {
- errno = GetLastError() == ERROR_ACCESS_DENIED ? EACCES : ENOENT;
- return -1;
- } else if (len > size) {
- errno = ENAMETOOLONG;
- return -1;
- }
-
- /* The Win32 APIs may return "\\?\" once you've used it first.
- * But it may not. What a gloriously predictible API!
- */
- if (wcsncmp(path, PATH__NT_NAMESPACE, PATH__NT_NAMESPACE_LEN))
- return len;
-
- len -= PATH__NT_NAMESPACE_LEN;
-
- memmove(path, path + PATH__NT_NAMESPACE_LEN, sizeof(wchar_t) * len);
- return len;
-}
-
-static wchar_t *path__skip_server(wchar_t *path)
-{
- wchar_t *c;
-
- for (c = path; *c; c++) {
- if (path__is_dirsep(*c))
- return c + 1;
- }
-
- return c;
-}
-
-static wchar_t *path__skip_prefix(wchar_t *path)
-{
- if (path__is_nt_namespace(path)) {
- path += PATH__NT_NAMESPACE_LEN;
-
- if (wcsncmp(path, L"UNC\\", 4) == 0)
- path = path__skip_server(path + 4);
- else if (path__is_absolute(path))
- path += PATH__ABSOLUTE_LEN;
- } else if (path__is_absolute(path)) {
- path += PATH__ABSOLUTE_LEN;
- } else if (path__is_unc(path)) {
- path = path__skip_server(path + 2);
- }
-
- return path;
-}
-
-int git_win32_path_canonicalize(git_win32_path path)
-{
- wchar_t *base, *from, *to, *next;
- size_t len;
-
- base = to = path__skip_prefix(path);
-
- /* Unposixify if the prefix */
- for (from = path; from < to; from++) {
- if (*from == L'/')
- *from = L'\\';
- }
-
- while (*from) {
- for (next = from; *next; ++next) {
- if (*next == L'/') {
- *next = L'\\';
- break;
- }
-
- if (*next == L'\\')
- break;
- }
-
- len = next - from;
-
- if (len == 1 && from[0] == L'.')
- /* do nothing with singleton dot */;
-
- else if (len == 2 && from[0] == L'.' && from[1] == L'.') {
- if (to == base) {
- /* no more path segments to strip, eat the "../" */
- if (*next == L'\\')
- len++;
-
- base = to;
- } else {
- /* back up a path segment */
- while (to > base && to[-1] == L'\\') to--;
- while (to > base && to[-1] != L'\\') to--;
- }
- } else {
- if (*next == L'\\' && *from != L'\\')
- len++;
-
- if (to != from)
- memmove(to, from, sizeof(wchar_t) * len);
-
- to += len;
- }
-
- from += len;
-
- while (*from == L'\\') from++;
- }
-
- /* Strip trailing backslashes */
- while (to > base && to[-1] == L'\\') to--;
-
- *to = L'\0';
-
- return (to - path);
-}
-
-int git_win32_path__cwd(wchar_t *out, size_t len)
-{
- int cwd_len;
-
- if ((cwd_len = path__cwd(out, len)) < 0)
- return -1;
-
- /* UNC paths */
- if (wcsncmp(L"\\\\", out, 2) == 0) {
- /* Our buffer must be at least 5 characters larger than the
- * current working directory: we swallow one of the leading
- * '\'s, but we we add a 'UNC' specifier to the path, plus
- * a trailing directory separator, plus a NUL.
- */
- if (cwd_len > MAX_PATH - 4) {
- errno = ENAMETOOLONG;
- return -1;
- }
-
- memmove(out+2, out, sizeof(wchar_t) * cwd_len);
- out[0] = L'U';
- out[1] = L'N';
- out[2] = L'C';
-
- cwd_len += 2;
- }
-
- /* Our buffer must be at least 2 characters larger than the current
- * working directory. (One character for the directory separator,
- * one for the null.
- */
- else if (cwd_len > MAX_PATH - 2) {
- errno = ENAMETOOLONG;
- return -1;
- }
-
- return cwd_len;
-}
-
-int git_win32_path_from_utf8(git_win32_path out, const char *src)
-{
- wchar_t *dest = out;
-
- /* All win32 paths are in NT-prefixed format, beginning with "\\?\". */
- memcpy(dest, PATH__NT_NAMESPACE, sizeof(wchar_t) * PATH__NT_NAMESPACE_LEN);
- dest += PATH__NT_NAMESPACE_LEN;
-
- /* See if this is an absolute path (beginning with a drive letter) */
- if (path__is_absolute(src)) {
- if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
- goto on_error;
- }
- /* File-prefixed NT-style paths beginning with \\?\ */
- else if (path__is_nt_namespace(src)) {
- /* Skip the NT prefix, the destination already contains it */
- if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0)
- goto on_error;
- }
- /* UNC paths */
- else if (path__is_unc(src)) {
- memcpy(dest, L"UNC\\", sizeof(wchar_t) * 4);
- dest += 4;
-
- /* Skip the leading "\\" */
- if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0)
- goto on_error;
- }
- /* Absolute paths omitting the drive letter */
- else if (src[0] == '\\' || src[0] == '/') {
- if (path__cwd(dest, MAX_PATH) < 0)
- goto on_error;
-
- if (!path__is_absolute(dest)) {
- errno = ENOENT;
- goto on_error;
- }
-
- /* Skip the drive letter specification ("C:") */
- if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0)
- goto on_error;
- }
- /* Relative paths */
- else {
- int cwd_len;
-
- if ((cwd_len = git_win32_path__cwd(dest, MAX_PATH)) < 0)
- goto on_error;
-
- dest[cwd_len++] = L'\\';
-
- if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0)
- goto on_error;
- }
-
- return git_win32_path_canonicalize(out);
-
-on_error:
- /* set windows error code so we can use its error message */
- if (errno == ENAMETOOLONG)
- SetLastError(ERROR_FILENAME_EXCED_RANGE);
-
- return -1;
-}
-
-int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
-{
- char *out = dest;
- int len;
-
- /* Strip NT namespacing "\\?\" */
- if (path__is_nt_namespace(src)) {
- src += 4;
-
- /* "\\?\UNC\server\share" -> "\\server\share" */
- if (wcsncmp(src, L"UNC\\", 4) == 0) {
- src += 4;
-
- memcpy(dest, "\\\\", 2);
- out = dest + 2;
- }
- }
-
- if ((len = git__utf16_to_8(out, GIT_WIN_PATH_UTF8, src)) < 0)
- return len;
-
- git_path_mkposix(dest);
-
- return len;
-}
-
-char *git_win32_path_8dot3_name(const char *path)
-{
- git_win32_path longpath, shortpath;
- wchar_t *start;
- char *shortname;
- int len, namelen = 1;
-
- if (git_win32_path_from_utf8(longpath, path) < 0)
- return NULL;
-
- len = GetShortPathNameW(longpath, shortpath, GIT_WIN_PATH_UTF16);
-
- while (len && shortpath[len-1] == L'\\')
- shortpath[--len] = L'\0';
-
- if (len == 0 || len >= GIT_WIN_PATH_UTF16)
- return NULL;
-
- for (start = shortpath + (len - 1);
- start > shortpath && *(start-1) != '/' && *(start-1) != '\\';
- start--)
- namelen++;
-
- /* We may not have actually been given a short name. But if we have,
- * it will be in the ASCII byte range, so we don't need to worry about
- * multi-byte sequences and can allocate naively.
- */
- if (namelen > 12 || (shortname = git__malloc(namelen + 1)) == NULL)
- return NULL;
-
- if ((len = git__utf16_to_8(shortname, namelen + 1, start)) < 0)
- return NULL;
-
- return shortname;
-}
-
-static bool path_is_volume(wchar_t *target, size_t target_len)
-{
- return (target_len && wcsncmp(target, L"\\??\\Volume{", 11) == 0);
-}
-
-/* On success, returns the length, in characters, of the path stored in dest.
-* On failure, returns a negative value. */
-int git_win32_path_readlink_w(git_win32_path dest, const git_win32_path path)
-{
- BYTE buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
- GIT_REPARSE_DATA_BUFFER *reparse_buf = (GIT_REPARSE_DATA_BUFFER *)buf;
- HANDLE handle = NULL;
- DWORD ioctl_ret;
- wchar_t *target;
- size_t target_len;
-
- int error = -1;
-
- handle = CreateFileW(path, GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
- FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
-
- if (handle == INVALID_HANDLE_VALUE) {
- errno = ENOENT;
- return -1;
- }
-
- if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
- reparse_buf, sizeof(buf), &ioctl_ret, NULL)) {
- errno = EINVAL;
- goto on_error;
- }
-
- switch (reparse_buf->ReparseTag) {
- case IO_REPARSE_TAG_SYMLINK:
- target = reparse_buf->SymbolicLinkReparseBuffer.PathBuffer +
- (reparse_buf->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
- target_len = reparse_buf->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
- break;
- case IO_REPARSE_TAG_MOUNT_POINT:
- target = reparse_buf->MountPointReparseBuffer.PathBuffer +
- (reparse_buf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
- target_len = reparse_buf->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
- break;
- default:
- errno = EINVAL;
- goto on_error;
- }
-
- if (path_is_volume(target, target_len)) {
- /* This path is a reparse point that represents another volume mounted
- * at this location, it is not a symbolic link our input was canonical.
- */
- errno = EINVAL;
- error = -1;
- } else if (target_len) {
- /* The path may need to have a prefix removed. */
- target_len = git_win32__canonicalize_path(target, target_len);
-
- /* Need one additional character in the target buffer
- * for the terminating NULL. */
- if (GIT_WIN_PATH_UTF16 > target_len) {
- wcscpy(dest, target);
- error = (int)target_len;
- }
- }
-
-on_error:
- CloseHandle(handle);
- return error;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_path_w32_h__
-#define INCLUDE_git_path_w32_h__
-
-#include "common.h"
-#include "vector.h"
-
-/*
- * Provides a large enough buffer to support Windows paths: MAX_PATH is
- * 260, corresponding to a maximum path length of 259 characters plus a
- * NULL terminator. Prefixing with "\\?\" adds 4 characters, but if the
- * original was a UNC path, then we turn "\\server\share" into
- * "\\?\UNC\server\share". So we replace the first two characters with
- * 8 characters, a net gain of 6, so the maximum length is MAX_PATH+6.
- */
-#define GIT_WIN_PATH_UTF16 MAX_PATH+6
-
-/* Maximum size of a UTF-8 Win32 path. We remove the "\\?\" or "\\?\UNC\"
- * prefixes for presentation, bringing us back to 259 (non-NULL)
- * characters. UTF-8 does have 4-byte sequences, but they are encoded in
- * UTF-16 using surrogate pairs, which takes up the space of two characters.
- * Two characters in the range U+0800 -> U+FFFF take up more space in UTF-8
- * (6 bytes) than one surrogate pair (4 bytes).
- */
-#define GIT_WIN_PATH_UTF8 (259 * 3 + 1)
-
-/*
- * The length of a Windows "shortname", for 8.3 compatibility.
- */
-#define GIT_WIN_PATH_SHORTNAME 13
-
-/* Win32 path types */
-typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16];
-typedef char git_win32_utf8_path[GIT_WIN_PATH_UTF8];
-
-/**
- * Create a Win32 path (in UCS-2 format) from a UTF-8 string.
- *
- * @param dest The buffer to receive the wide string.
- * @param src The UTF-8 string to convert.
- * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
- */
-extern int git_win32_path_from_utf8(git_win32_path dest, const char *src);
-
-/**
- * Canonicalize a Win32 UCS-2 path so that it is suitable for delivery to the
- * Win32 APIs: remove multiple directory separators, squashing to a single one,
- * strip trailing directory separators, ensure directory separators are all
- * canonical (always backslashes, never forward slashes) and process any
- * directory entries of '.' or '..'.
- *
- * This processes the buffer in place.
- *
- * @param path The buffer to process
- * @return The new length of the buffer, in wchar_t's (not counting the NULL terminator)
- */
-extern int git_win32_path_canonicalize(git_win32_path path);
-
-/**
- * Create an internal format (posix-style) UTF-8 path from a Win32 UCS-2 path.
- *
- * @param dest The buffer to receive the UTF-8 string.
- * @param src The wide string to convert.
- * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
- */
-extern int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src);
-
-/**
- * Get the short name for the terminal path component in the given path.
- * For example, given "C:\Foo\Bar\Asdf.txt", this will return the short name
- * for the file "Asdf.txt".
- *
- * @param path The given path in UTF-8
- * @return The name of the shortname for the given path
- */
-extern char *git_win32_path_8dot3_name(const char *path);
-
-extern int git_win32_path_readlink_w(git_win32_path dest, const git_win32_path path);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_posix__w32_h__
-#define INCLUDE_posix__w32_h__
-
-#include "common.h"
-#include "../posix.h"
-#include "win32-compat.h"
-#include "path_w32.h"
-#include "utf-conv.h"
-#include "dir.h"
-
-typedef SOCKET GIT_SOCKET;
-
-#define p_lseek(f,n,w) _lseeki64(f, n, w)
-
-extern int p_fstat(int fd, struct stat *buf);
-extern int p_lstat(const char *file_name, struct stat *buf);
-extern int p_stat(const char* path, struct stat *buf);
-
-extern int p_utimes(const char *filename, const struct p_timeval times[2]);
-extern int p_futimes(int fd, const struct p_timeval times[2]);
-
-extern int p_readlink(const char *path, char *buf, size_t bufsiz);
-extern int p_symlink(const char *old, const char *new);
-extern int p_link(const char *old, const char *new);
-extern int p_unlink(const char *path);
-extern int p_mkdir(const char *path, mode_t mode);
-extern int p_fsync(int fd);
-extern char *p_realpath(const char *orig_path, char *buffer);
-
-extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);
-extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);
-extern int p_inet_pton(int af, const char* src, void* dst);
-
-extern int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr);
-extern int p_snprintf(char *buffer, size_t count, const char *format, ...) GIT_FORMAT_PRINTF(3, 4);
-extern int p_mkstemp(char *tmp_path);
-extern int p_chdir(const char* path);
-extern int p_chmod(const char* path, mode_t mode);
-extern int p_rmdir(const char* path);
-extern int p_access(const char* path, mode_t mode);
-extern int p_ftruncate(int fd, git_off_t size);
-
-/* p_lstat is almost but not quite POSIX correct. Specifically, the use of
- * ENOTDIR is wrong, in that it does not mean precisely that a non-directory
- * entry was encountered. Making it correct is potentially expensive,
- * however, so this is a separate version of p_lstat to use when correct
- * POSIX ENOTDIR semantics is required.
- */
-extern int p_lstat_posixly(const char *filename, struct stat *buf);
-
-extern struct tm * p_localtime_r(const time_t *timer, struct tm *result);
-extern struct tm * p_gmtime_r(const time_t *timer, struct tm *result);
-
-/* Use the bundled regcomp */
-#define p_regcomp regcomp
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "../posix.h"
-#include "../fileops.h"
-#include "path.h"
-#include "path_w32.h"
-#include "utf-conv.h"
-#include "repository.h"
-#include "reparse.h"
-#include "global.h"
-#include "buffer.h"
-#include <errno.h>
-#include <io.h>
-#include <fcntl.h>
-#include <ws2tcpip.h>
-
-#ifndef FILE_NAME_NORMALIZED
-# define FILE_NAME_NORMALIZED 0
-#endif
-
-#ifndef IO_REPARSE_TAG_SYMLINK
-#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
-#endif
-
-/* Options which we always provide to _wopen.
- *
- * _O_BINARY - Raw access; no translation of CR or LF characters
- * _O_NOINHERIT - Do not mark the created handle as inheritable by child processes.
- * The Windows default is 'not inheritable', but the CRT's default (following
- * POSIX convention) is 'inheritable'. We have no desire for our handles to be
- * inheritable on Windows, so specify the flag to get default behavior back. */
-#define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT)
-
-/* Allowable mode bits on Win32. Using mode bits that are not supported on
- * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
- * so we simply remove them.
- */
-#define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)
-
-/* GetFinalPathNameByHandleW signature */
-typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
-
-/**
- * Truncate or extend file.
- *
- * We now take a "git_off_t" rather than "long" because
- * files may be longer than 2Gb.
- */
-int p_ftruncate(int fd, git_off_t size)
-{
- if (size < 0) {
- errno = EINVAL;
- return -1;
- }
-
-#if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
- return ((_chsize_s(fd, size) == 0) ? 0 : -1);
-#else
- /* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
- if (size > INT32_MAX) {
- errno = EFBIG;
- return -1;
- }
- return _chsize(fd, (long)size);
-#endif
-}
-
-int p_mkdir(const char *path, mode_t mode)
-{
- git_win32_path buf;
-
- GIT_UNUSED(mode);
-
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
-
- return _wmkdir(buf);
-}
-
-int p_link(const char *old, const char *new)
-{
- GIT_UNUSED(old);
- GIT_UNUSED(new);
- errno = ENOSYS;
- return -1;
-}
-
-int p_unlink(const char *path)
-{
- git_win32_path buf;
- int error;
-
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
-
- error = _wunlink(buf);
-
- /* If the file could not be deleted because it was
- * read-only, clear the bit and try again */
- if (error == -1 && errno == EACCES) {
- _wchmod(buf, 0666);
- error = _wunlink(buf);
- }
-
- return error;
-}
-
-int p_fsync(int fd)
-{
- HANDLE fh = (HANDLE)_get_osfhandle(fd);
-
- if (fh == INVALID_HANDLE_VALUE) {
- errno = EBADF;
- return -1;
- }
-
- if (!FlushFileBuffers(fh)) {
- DWORD code = GetLastError();
-
- if (code == ERROR_INVALID_HANDLE)
- errno = EINVAL;
- else
- errno = EIO;
-
- return -1;
- }
-
- return 0;
-}
-
-#define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
-
-static int lstat_w(
- wchar_t *path,
- struct stat *buf,
- bool posix_enotdir)
-{
- WIN32_FILE_ATTRIBUTE_DATA fdata;
-
- if (GetFileAttributesExW(path, GetFileExInfoStandard, &fdata)) {
- if (!buf)
- return 0;
-
- return git_win32__file_attribute_to_stat(buf, &fdata, path);
- }
-
- switch (GetLastError()) {
- case ERROR_ACCESS_DENIED:
- errno = EACCES;
- break;
- default:
- errno = ENOENT;
- break;
- }
-
- /* To match POSIX behavior, set ENOTDIR when any of the folders in the
- * file path is a regular file, otherwise set ENOENT.
- */
- if (errno == ENOENT && posix_enotdir) {
- size_t path_len = wcslen(path);
-
- /* scan up path until we find an existing item */
- while (1) {
- DWORD attrs;
-
- /* remove last directory component */
- for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--);
-
- if (path_len <= 0)
- break;
-
- path[path_len] = L'\0';
- attrs = GetFileAttributesW(path);
-
- if (attrs != INVALID_FILE_ATTRIBUTES) {
- if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
- errno = ENOTDIR;
- break;
- }
- }
- }
-
- return -1;
-}
-
-static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
-{
- git_win32_path path_w;
- int len;
-
- if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
- return -1;
-
- git_win32__path_trim_end(path_w, len);
-
- return lstat_w(path_w, buf, posixly_correct);
-}
-
-int p_lstat(const char *filename, struct stat *buf)
-{
- return do_lstat(filename, buf, false);
-}
-
-int p_lstat_posixly(const char *filename, struct stat *buf)
-{
- return do_lstat(filename, buf, true);
-}
-
-int p_utimes(const char *filename, const struct p_timeval times[2])
-{
- int fd, error;
-
- if ((fd = p_open(filename, O_RDWR)) < 0)
- return fd;
-
- error = p_futimes(fd, times);
-
- close(fd);
- return error;
-}
-
-int p_futimes(int fd, const struct p_timeval times[2])
-{
- HANDLE handle;
- FILETIME atime = {0}, mtime = {0};
-
- if (times == NULL) {
- SYSTEMTIME st;
-
- GetSystemTime(&st);
- SystemTimeToFileTime(&st, &atime);
- SystemTimeToFileTime(&st, &mtime);
- } else {
- git_win32__timeval_to_filetime(&atime, times[0]);
- git_win32__timeval_to_filetime(&mtime, times[1]);
- }
-
- if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
- return -1;
-
- if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
- return -1;
-
- return 0;
-}
-
-int p_readlink(const char *path, char *buf, size_t bufsiz)
-{
- git_win32_path path_w, target_w;
- git_win32_utf8_path target;
- int len;
-
- /* readlink(2) does not NULL-terminate the string written
- * to the target buffer. Furthermore, the target buffer need
- * not be large enough to hold the entire result. A truncated
- * result should be written in this case. Since this truncation
- * could occur in the middle of the encoding of a code point,
- * we need to buffer the result on the stack. */
-
- if (git_win32_path_from_utf8(path_w, path) < 0 ||
- git_win32_path_readlink_w(target_w, path_w) < 0 ||
- (len = git_win32_path_to_utf8(target, target_w)) < 0)
- return -1;
-
- bufsiz = min((size_t)len, bufsiz);
- memcpy(buf, target, bufsiz);
-
- return (int)bufsiz;
-}
-
-int p_symlink(const char *old, const char *new)
-{
- /* Real symlinks on NTFS require admin privileges. Until this changes,
- * libgit2 just creates a text file with the link target in the contents.
- */
- return git_futils_fake_symlink(old, new);
-}
-
-int p_open(const char *path, int flags, ...)
-{
- git_win32_path buf;
- mode_t mode = 0;
-
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
-
- if (flags & O_CREAT) {
- va_list arg_list;
-
- va_start(arg_list, flags);
- mode = (mode_t)va_arg(arg_list, int);
- va_end(arg_list);
- }
-
- return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode & WIN32_MODE_MASK);
-}
-
-int p_creat(const char *path, mode_t mode)
-{
- git_win32_path buf;
-
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
-
- return _wopen(buf,
- _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS,
- mode & WIN32_MODE_MASK);
-}
-
-int p_getcwd(char *buffer_out, size_t size)
-{
- git_win32_path buf;
- wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
-
- if (!cwd)
- return -1;
-
- /* Convert the working directory back to UTF-8 */
- if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
- DWORD code = GetLastError();
-
- if (code == ERROR_INSUFFICIENT_BUFFER)
- errno = ERANGE;
- else
- errno = EINVAL;
-
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Returns the address of the GetFinalPathNameByHandleW function.
- * This function is available on Windows Vista and higher.
- */
-static PFGetFinalPathNameByHandleW get_fpnbyhandle(void)
-{
- static PFGetFinalPathNameByHandleW pFunc = NULL;
- PFGetFinalPathNameByHandleW toReturn = pFunc;
-
- if (!toReturn) {
- HMODULE hModule = GetModuleHandleW(L"kernel32");
-
- if (hModule)
- toReturn = (PFGetFinalPathNameByHandleW)GetProcAddress(hModule, "GetFinalPathNameByHandleW");
-
- pFunc = toReturn;
- }
-
- assert(toReturn);
-
- return toReturn;
-}
-
-static int getfinalpath_w(
- git_win32_path dest,
- const wchar_t *path)
-{
- PFGetFinalPathNameByHandleW pgfp = get_fpnbyhandle();
- HANDLE hFile;
- DWORD dwChars;
-
- if (!pgfp)
- return -1;
-
- /* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
- * specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
- * target of the link. */
- hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
- NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-
- if (INVALID_HANDLE_VALUE == hFile)
- return -1;
-
- /* Call GetFinalPathNameByHandle */
- dwChars = pgfp(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED);
- CloseHandle(hFile);
-
- if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
- return -1;
-
- /* The path may be delivered to us with a prefix; canonicalize */
- return (int)git_win32__canonicalize_path(dest, dwChars);
-}
-
-static int follow_and_lstat_link(git_win32_path path, struct stat* buf)
-{
- git_win32_path target_w;
-
- if (getfinalpath_w(target_w, path) < 0)
- return -1;
-
- return lstat_w(target_w, buf, false);
-}
-
-int p_fstat(int fd, struct stat *buf)
-{
- BY_HANDLE_FILE_INFORMATION fhInfo;
-
- HANDLE fh = (HANDLE)_get_osfhandle(fd);
-
- if (fh == INVALID_HANDLE_VALUE ||
- !GetFileInformationByHandle(fh, &fhInfo)) {
- errno = EBADF;
- return -1;
- }
-
- git_win32__file_information_to_stat(buf, &fhInfo);
- return 0;
-}
-
-int p_stat(const char* path, struct stat* buf)
-{
- git_win32_path path_w;
- int len;
-
- if ((len = git_win32_path_from_utf8(path_w, path)) < 0 ||
- lstat_w(path_w, buf, false) < 0)
- return -1;
-
- /* The item is a symbolic link or mount point. No need to iterate
- * to follow multiple links; use GetFinalPathNameFromHandle. */
- if (S_ISLNK(buf->st_mode))
- return follow_and_lstat_link(path_w, buf);
-
- return 0;
-}
-
-int p_chdir(const char* path)
-{
- git_win32_path buf;
-
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
-
- return _wchdir(buf);
-}
-
-int p_chmod(const char* path, mode_t mode)
-{
- git_win32_path buf;
-
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
-
- return _wchmod(buf, mode);
-}
-
-int p_rmdir(const char* path)
-{
- git_win32_path buf;
- int error;
-
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
-
- error = _wrmdir(buf);
-
- if (error == -1) {
- switch (GetLastError()) {
- /* _wrmdir() is documented to return EACCES if "A program has an open
- * handle to the directory." This sounds like what everybody else calls
- * EBUSY. Let's convert appropriate error codes.
- */
- case ERROR_SHARING_VIOLATION:
- errno = EBUSY;
- break;
-
- /* This error can be returned when trying to rmdir an extant file. */
- case ERROR_DIRECTORY:
- errno = ENOTDIR;
- break;
- }
- }
-
- return error;
-}
-
-char *p_realpath(const char *orig_path, char *buffer)
-{
- git_win32_path orig_path_w, buffer_w;
-
- if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
- return NULL;
-
- /* Note that if the path provided is a relative path, then the current directory
- * is used to resolve the path -- which is a concurrency issue because the current
- * directory is a process-wide variable. */
- if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
- errno = ENAMETOOLONG;
- else
- errno = EINVAL;
-
- return NULL;
- }
-
- /* The path must exist. */
- if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
- errno = ENOENT;
- return NULL;
- }
-
- if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
- errno = ENOMEM;
- return NULL;
- }
-
- /* Convert the path to UTF-8. If the caller provided a buffer, then it
- * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
- * then we may overflow. */
- if (git_win32_path_to_utf8(buffer, buffer_w) < 0)
- return NULL;
-
- git_path_mkposix(buffer);
-
- return buffer;
-}
-
-int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
-{
-#if defined(_MSC_VER)
- int len;
-
- if (count == 0)
- return _vscprintf(format, argptr);
-
- #if _MSC_VER >= 1500
- len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
- #else
- len = _vsnprintf(buffer, count, format, argptr);
- #endif
-
- if (len < 0)
- return _vscprintf(format, argptr);
-
- return len;
-#else /* MinGW */
- return vsnprintf(buffer, count, format, argptr);
-#endif
-}
-
-int p_snprintf(char *buffer, size_t count, const char *format, ...)
-{
- va_list va;
- int r;
-
- va_start(va, format);
- r = p_vsnprintf(buffer, count, format, va);
- va_end(va);
-
- return r;
-}
-
-/* TODO: wut? */
-int p_mkstemp(char *tmp_path)
-{
-#if defined(_MSC_VER) && _MSC_VER >= 1500
- if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
- return -1;
-#else
- if (_mktemp(tmp_path) == NULL)
- return -1;
-#endif
-
- return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); //-V536
-}
-
-int p_access(const char* path, mode_t mode)
-{
- git_win32_path buf;
-
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
-
- return _waccess(buf, mode & WIN32_MODE_MASK);
-}
-
-static int ensure_writable(wchar_t *fpath)
-{
- DWORD attrs;
-
- attrs = GetFileAttributesW(fpath);
- if (attrs == INVALID_FILE_ATTRIBUTES) {
- if (GetLastError() == ERROR_FILE_NOT_FOUND)
- return 0;
-
- giterr_set(GITERR_OS, "failed to get attributes");
- return -1;
- }
-
- if (!(attrs & FILE_ATTRIBUTE_READONLY))
- return 0;
-
- attrs &= ~FILE_ATTRIBUTE_READONLY;
- if (!SetFileAttributesW(fpath, attrs)) {
- giterr_set(GITERR_OS, "failed to set attributes");
- return -1;
- }
-
- return 0;
-}
-
-int p_rename(const char *from, const char *to)
-{
- git_win32_path wfrom;
- git_win32_path wto;
- int rename_tries;
- int rename_succeeded;
- int error;
-
- if (git_win32_path_from_utf8(wfrom, from) < 0 ||
- git_win32_path_from_utf8(wto, to) < 0)
- return -1;
-
- /* wait up to 50ms if file is locked by another thread or process */
- rename_tries = 0;
- rename_succeeded = 0;
- while (rename_tries < 10) {
- if (ensure_writable(wto) == 0 &&
- MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
- rename_succeeded = 1;
- break;
- }
-
- error = GetLastError();
- if (error == ERROR_SHARING_VIOLATION || error == ERROR_ACCESS_DENIED) {
- Sleep(5);
- rename_tries++;
- } else
- break;
- }
-
- return rename_succeeded ? 0 : -1;
-}
-
-int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
-{
- if ((size_t)((int)length) != length)
- return -1; /* giterr_set will be done by caller */
-
- return recv(socket, buffer, (int)length, flags);
-}
-
-int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
-{
- if ((size_t)((int)length) != length)
- return -1; /* giterr_set will be done by caller */
-
- return send(socket, buffer, (int)length, flags);
-}
-
-/**
- * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
- * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
- */
-struct tm *
-p_localtime_r (const time_t *timer, struct tm *result)
-{
- struct tm *local_result;
- local_result = localtime (timer);
-
- if (local_result == NULL || result == NULL)
- return NULL;
-
- memcpy (result, local_result, sizeof (struct tm));
- return result;
-}
-struct tm *
-p_gmtime_r (const time_t *timer, struct tm *result)
-{
- struct tm *local_result;
- local_result = gmtime (timer);
-
- if (local_result == NULL || result == NULL)
- return NULL;
-
- memcpy (result, local_result, sizeof (struct tm));
- return result;
-}
-
-int p_inet_pton(int af, const char *src, void *dst)
-{
- struct sockaddr_storage sin;
- void *addr;
- int sin_len = sizeof(struct sockaddr_storage), addr_len;
- int error = 0;
-
- if (af == AF_INET) {
- addr = &((struct sockaddr_in *)&sin)->sin_addr;
- addr_len = sizeof(struct in_addr);
- } else if (af == AF_INET6) {
- addr = &((struct sockaddr_in6 *)&sin)->sin6_addr;
- addr_len = sizeof(struct in6_addr);
- } else {
- errno = EAFNOSUPPORT;
- return -1;
- }
-
- if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
- memcpy(dst, addr, addr_len);
- return 1;
- }
-
- switch(WSAGetLastError()) {
- case WSAEINVAL:
- return 0;
- case WSAEFAULT:
- errno = ENOSPC;
- return -1;
- case WSA_NOT_ENOUGH_MEMORY:
- errno = ENOMEM;
- return -1;
- }
-
- errno = EINVAL;
- return -1;
-}
+++ /dev/null
-#include "precompiled.h"
+++ /dev/null
-#include <assert.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <time.h>
-#include <stdarg.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <regex.h>
-
-#include <io.h>
-#include <direct.h>
-#ifdef GIT_THREADS
- #include "win32/thread.h"
-#endif
-
-#include "git2.h"
-#include "common.h"
+++ /dev/null
-/*
-* Copyright (C) the libgit2 contributors. All rights reserved.
-*
-* This file is part of libgit2, distributed under the GNU GPL v2 with
-* a Linking Exception. For full terms see the included COPYING file.
-*/
-
-#ifndef INCLUDE_git_win32_reparse_h__
-#define INCLUDE_git_win32_reparse_h__
-
-/* This structure is defined on MSDN at
-* http://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
-*
-* It was formerly included in the Windows 2000 SDK and remains defined in
-* MinGW, so we must define it with a silly name to avoid conflicting.
-*/
-typedef struct _GIT_REPARSE_DATA_BUFFER {
- ULONG ReparseTag;
- USHORT ReparseDataLength;
- USHORT Reserved;
- union {
- struct {
- USHORT SubstituteNameOffset;
- USHORT SubstituteNameLength;
- USHORT PrintNameOffset;
- USHORT PrintNameLength;
- ULONG Flags;
- WCHAR PathBuffer[1];
- } SymbolicLinkReparseBuffer;
- struct {
- USHORT SubstituteNameOffset;
- USHORT SubstituteNameLength;
- USHORT PrintNameOffset;
- USHORT PrintNameLength;
- WCHAR PathBuffer[1];
- } MountPointReparseBuffer;
- struct {
- UCHAR DataBuffer[1];
- } GenericReparseBuffer;
- };
-} GIT_REPARSE_DATA_BUFFER;
-
-#define REPARSE_DATA_HEADER_SIZE 8
-#define REPARSE_DATA_MOUNTPOINT_HEADER_SIZE 8
-#define REPARSE_DATA_UNION_SIZE 12
-
-/* Missing in MinGW */
-#ifndef FSCTL_GET_REPARSE_POINT
-# define FSCTL_GET_REPARSE_POINT 0x000900a8
-#endif
-
-/* Missing in MinGW */
-#ifndef FSCTL_SET_REPARSE_POINT
-# define FSCTL_SET_REPARSE_POINT 0x000900a4
-#endif
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "thread.h"
-#include "../global.h"
-
-#define CLEAN_THREAD_EXIT 0x6F012842
-
-typedef void (WINAPI *win32_srwlock_fn)(GIT_SRWLOCK *);
-
-static win32_srwlock_fn win32_srwlock_initialize;
-static win32_srwlock_fn win32_srwlock_acquire_shared;
-static win32_srwlock_fn win32_srwlock_release_shared;
-static win32_srwlock_fn win32_srwlock_acquire_exclusive;
-static win32_srwlock_fn win32_srwlock_release_exclusive;
-
-/* The thread procedure stub used to invoke the caller's procedure
- * and capture the return value for later collection. Windows will
- * only hold a DWORD, but we need to be able to store an entire
- * void pointer. This requires the indirection. */
-static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter)
-{
- git_thread *thread = lpParameter;
-
- /* Set the current thread for `git_thread_exit` */
- GIT_GLOBAL->current_thread = thread;
-
- thread->result = thread->proc(thread->param);
-
- git__free_tls_data();
-
- return CLEAN_THREAD_EXIT;
-}
-
-int git_threads_init(void)
-{
- HMODULE hModule = GetModuleHandleW(L"kernel32");
-
- if (hModule) {
- win32_srwlock_initialize = (win32_srwlock_fn)
- GetProcAddress(hModule, "InitializeSRWLock");
- win32_srwlock_acquire_shared = (win32_srwlock_fn)
- GetProcAddress(hModule, "AcquireSRWLockShared");
- win32_srwlock_release_shared = (win32_srwlock_fn)
- GetProcAddress(hModule, "ReleaseSRWLockShared");
- win32_srwlock_acquire_exclusive = (win32_srwlock_fn)
- GetProcAddress(hModule, "AcquireSRWLockExclusive");
- win32_srwlock_release_exclusive = (win32_srwlock_fn)
- GetProcAddress(hModule, "ReleaseSRWLockExclusive");
- }
-
- return 0;
-}
-
-int git_thread_create(
- git_thread *GIT_RESTRICT thread,
- void *(*start_routine)(void*),
- void *GIT_RESTRICT arg)
-{
- thread->result = NULL;
- thread->param = arg;
- thread->proc = start_routine;
- thread->thread = CreateThread(
- NULL, 0, git_win32__threadproc, thread, 0, NULL);
-
- return thread->thread ? 0 : -1;
-}
-
-int git_thread_join(
- git_thread *thread,
- void **value_ptr)
-{
- DWORD exit;
-
- if (WaitForSingleObject(thread->thread, INFINITE) != WAIT_OBJECT_0)
- return -1;
-
- if (!GetExitCodeThread(thread->thread, &exit)) {
- CloseHandle(thread->thread);
- return -1;
- }
-
- /* Check for the thread having exited uncleanly. If exit was unclean,
- * then we don't have a return value to give back to the caller. */
- if (exit != CLEAN_THREAD_EXIT) {
- assert(false);
- thread->result = NULL;
- }
-
- if (value_ptr)
- *value_ptr = thread->result;
-
- CloseHandle(thread->thread);
- return 0;
-}
-
-void git_thread_exit(void *value)
-{
- assert(GIT_GLOBAL->current_thread);
- GIT_GLOBAL->current_thread->result = value;
-
- git__free_tls_data();
-
- ExitThread(CLEAN_THREAD_EXIT);
-}
-
-size_t git_thread_currentid(void)
-{
- return GetCurrentThreadId();
-}
-
-int git_mutex_init(git_mutex *GIT_RESTRICT mutex)
-{
- InitializeCriticalSection(mutex);
- return 0;
-}
-
-int git_mutex_free(git_mutex *mutex)
-{
- DeleteCriticalSection(mutex);
- return 0;
-}
-
-int git_mutex_lock(git_mutex *mutex)
-{
- EnterCriticalSection(mutex);
- return 0;
-}
-
-int git_mutex_unlock(git_mutex *mutex)
-{
- LeaveCriticalSection(mutex);
- return 0;
-}
-
-int git_cond_init(git_cond *cond)
-{
- /* This is an auto-reset event. */
- *cond = CreateEventW(NULL, FALSE, FALSE, NULL);
- assert(*cond);
-
- /* If we can't create the event, claim that the reason was out-of-memory.
- * The actual reason can be fetched with GetLastError(). */
- return *cond ? 0 : ENOMEM;
-}
-
-int git_cond_free(git_cond *cond)
-{
- BOOL closed;
-
- if (!cond)
- return EINVAL;
-
- closed = CloseHandle(*cond);
- assert(closed);
- GIT_UNUSED(closed);
-
- *cond = NULL;
- return 0;
-}
-
-int git_cond_wait(git_cond *cond, git_mutex *mutex)
-{
- int error;
- DWORD wait_result;
-
- if (!cond || !mutex)
- return EINVAL;
-
- /* The caller must be holding the mutex. */
- error = git_mutex_unlock(mutex);
-
- if (error)
- return error;
-
- wait_result = WaitForSingleObject(*cond, INFINITE);
- assert(WAIT_OBJECT_0 == wait_result);
- GIT_UNUSED(wait_result);
-
- return git_mutex_lock(mutex);
-}
-
-int git_cond_signal(git_cond *cond)
-{
- BOOL signaled;
-
- if (!cond)
- return EINVAL;
-
- signaled = SetEvent(*cond);
- assert(signaled);
- GIT_UNUSED(signaled);
-
- return 0;
-}
-
-int git_rwlock_init(git_rwlock *GIT_RESTRICT lock)
-{
- if (win32_srwlock_initialize)
- win32_srwlock_initialize(&lock->native.srwl);
- else
- InitializeCriticalSection(&lock->native.csec);
-
- return 0;
-}
-
-int git_rwlock_rdlock(git_rwlock *lock)
-{
- if (win32_srwlock_acquire_shared)
- win32_srwlock_acquire_shared(&lock->native.srwl);
- else
- EnterCriticalSection(&lock->native.csec);
-
- return 0;
-}
-
-int git_rwlock_rdunlock(git_rwlock *lock)
-{
- if (win32_srwlock_release_shared)
- win32_srwlock_release_shared(&lock->native.srwl);
- else
- LeaveCriticalSection(&lock->native.csec);
-
- return 0;
-}
-
-int git_rwlock_wrlock(git_rwlock *lock)
-{
- if (win32_srwlock_acquire_exclusive)
- win32_srwlock_acquire_exclusive(&lock->native.srwl);
- else
- EnterCriticalSection(&lock->native.csec);
-
- return 0;
-}
-
-int git_rwlock_wrunlock(git_rwlock *lock)
-{
- if (win32_srwlock_release_exclusive)
- win32_srwlock_release_exclusive(&lock->native.srwl);
- else
- LeaveCriticalSection(&lock->native.csec);
-
- return 0;
-}
-
-int git_rwlock_free(git_rwlock *lock)
-{
- if (!win32_srwlock_initialize)
- DeleteCriticalSection(&lock->native.csec);
- git__memzero(lock, sizeof(*lock));
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_win32_thread_h__
-#define INCLUDE_win32_thread_h__
-
-#include "../common.h"
-
-#if defined (_MSC_VER)
-# define GIT_RESTRICT __restrict
-#else
-# define GIT_RESTRICT __restrict__
-#endif
-
-typedef struct {
- HANDLE thread;
- void *(*proc)(void *);
- void *param;
- void *result;
-} git_thread;
-
-typedef CRITICAL_SECTION git_mutex;
-typedef HANDLE git_cond;
-
-typedef struct { void *Ptr; } GIT_SRWLOCK;
-
-typedef struct {
- union {
- GIT_SRWLOCK srwl;
- CRITICAL_SECTION csec;
- } native;
-} git_rwlock;
-
-int git_threads_init(void);
-
-int git_thread_create(git_thread *GIT_RESTRICT,
- void *(*) (void *),
- void *GIT_RESTRICT);
-int git_thread_join(git_thread *, void **);
-size_t git_thread_currentid(void);
-void git_thread_exit(void *);
-
-int git_mutex_init(git_mutex *GIT_RESTRICT mutex);
-int git_mutex_free(git_mutex *);
-int git_mutex_lock(git_mutex *);
-int git_mutex_unlock(git_mutex *);
-
-int git_cond_init(git_cond *);
-int git_cond_free(git_cond *);
-int git_cond_wait(git_cond *, git_mutex *);
-int git_cond_signal(git_cond *);
-
-int git_rwlock_init(git_rwlock *GIT_RESTRICT lock);
-int git_rwlock_rdlock(git_rwlock *);
-int git_rwlock_rdunlock(git_rwlock *);
-int git_rwlock_wrlock(git_rwlock *);
-int git_rwlock_wrunlock(git_rwlock *);
-int git_rwlock_free(git_rwlock *);
-
-#endif /* INCLUDE_win32_thread_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "utf-conv.h"
-
-GIT_INLINE(void) git__set_errno(void)
-{
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
- errno = ENAMETOOLONG;
- else
- errno = EINVAL;
-}
-
-/**
- * Converts a UTF-8 string to wide characters.
- *
- * @param dest The buffer to receive the wide string.
- * @param dest_size The size of the buffer, in characters.
- * @param src The UTF-8 string to convert.
- * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src)
-{
- int len;
-
- /* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to
- * turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's
- * length. MultiByteToWideChar never returns int's minvalue, so underflow is not possible */
- if ((len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size) - 1) < 0)
- git__set_errno();
-
- return len;
-}
-
-/**
- * Converts a wide string to UTF-8.
- *
- * @param dest The buffer to receive the UTF-8 string.
- * @param dest_size The size of the buffer, in bytes.
- * @param src The wide string to convert.
- * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src)
-{
- int len;
-
- /* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to
- * turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's
- * length. WideCharToMultiByte never returns int's minvalue, so underflow is not possible */
- if ((len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size, NULL, NULL) - 1) < 0)
- git__set_errno();
-
- return len;
-}
-
-/**
- * Converts a UTF-8 string to wide characters.
- * Memory is allocated to hold the converted string.
- * The caller is responsible for freeing the string with git__free.
- *
- * @param dest Receives a pointer to the wide string.
- * @param src The UTF-8 string to convert.
- * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf8_to_16_alloc(wchar_t **dest, const char *src)
-{
- int utf16_size;
-
- *dest = NULL;
-
- /* Length of -1 indicates NULL termination of the input string */
- utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0);
-
- if (!utf16_size) {
- git__set_errno();
- return -1;
- }
-
- if (!(*dest = git__mallocarray(utf16_size, sizeof(wchar_t)))) {
- errno = ENOMEM;
- return -1;
- }
-
- utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, *dest, utf16_size);
-
- if (!utf16_size) {
- git__set_errno();
-
- git__free(*dest);
- *dest = NULL;
- }
-
- /* Subtract 1 from the result to turn 0 into -1 (an error code) and to not count the NULL
- * terminator as part of the string's length. MultiByteToWideChar never returns int's minvalue,
- * so underflow is not possible */
- return utf16_size - 1;
-}
-
-/**
- * Converts a wide string to UTF-8.
- * Memory is allocated to hold the converted string.
- * The caller is responsible for freeing the string with git__free.
- *
- * @param dest Receives a pointer to the UTF-8 string.
- * @param src The wide string to convert.
- * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf16_to_8_alloc(char **dest, const wchar_t *src)
-{
- int utf8_size;
-
- *dest = NULL;
-
- /* Length of -1 indicates NULL termination of the input string */
- utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL);
-
- if (!utf8_size) {
- git__set_errno();
- return -1;
- }
-
- *dest = git__malloc(utf8_size);
-
- if (!*dest) {
- errno = ENOMEM;
- return -1;
- }
-
- utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, *dest, utf8_size, NULL, NULL);
-
- if (!utf8_size) {
- git__set_errno();
-
- git__free(*dest);
- *dest = NULL;
- }
-
- /* Subtract 1 from the result to turn 0 into -1 (an error code) and to not count the NULL
- * terminator as part of the string's length. MultiByteToWideChar never returns int's minvalue,
- * so underflow is not possible */
- return utf8_size - 1;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_utfconv_h__
-#define INCLUDE_git_utfconv_h__
-
-#include <wchar.h>
-#include "common.h"
-
-#ifndef WC_ERR_INVALID_CHARS
-# define WC_ERR_INVALID_CHARS 0x80
-#endif
-
-/**
- * Converts a UTF-8 string to wide characters.
- *
- * @param dest The buffer to receive the wide string.
- * @param dest_size The size of the buffer, in characters.
- * @param src The UTF-8 string to convert.
- * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src);
-
-/**
- * Converts a wide string to UTF-8.
- *
- * @param dest The buffer to receive the UTF-8 string.
- * @param dest_size The size of the buffer, in bytes.
- * @param src The wide string to convert.
- * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src);
-
-/**
- * Converts a UTF-8 string to wide characters.
- * Memory is allocated to hold the converted string.
- * The caller is responsible for freeing the string with git__free.
- *
- * @param dest Receives a pointer to the wide string.
- * @param src The UTF-8 string to convert.
- * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf8_to_16_alloc(wchar_t **dest, const char *src);
-
-/**
- * Converts a wide string to UTF-8.
- * Memory is allocated to hold the converted string.
- * The caller is responsible for freeing the string with git__free.
- *
- * @param dest Receives a pointer to the UTF-8 string.
- * @param src The wide string to convert.
- * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
- */
-int git__utf16_to_8_alloc(char **dest, const wchar_t *src);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_win32_version_h__
-#define INCLUDE_win32_version_h__
-
-#include <windows.h>
-
-GIT_INLINE(int) git_has_win32_version(int major, int minor, int service_pack)
-{
- OSVERSIONINFOEX version_test = {0};
- DWORD version_test_mask;
- DWORDLONG version_condition_mask = 0;
-
- version_test.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
- version_test.dwMajorVersion = major;
- version_test.dwMinorVersion = minor;
- version_test.wServicePackMajor = (WORD)service_pack;
- version_test.wServicePackMinor = 0;
-
- version_test_mask = (VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR);
-
- VER_SET_CONDITION(version_condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
- VER_SET_CONDITION(version_condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
- VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
- VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
-
- if (!VerifyVersionInfo(&version_test, version_test_mask, version_condition_mask))
- return 0;
-
- return 1;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "w32_buffer.h"
-#include "../buffer.h"
-#include "utf-conv.h"
-
-GIT_INLINE(int) handle_wc_error(void)
-{
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
- errno = ENAMETOOLONG;
- else
- errno = EINVAL;
-
- return -1;
-}
-
-int git_buf_put_w(git_buf *buf, const wchar_t *string_w, size_t len_w)
-{
- int utf8_len, utf8_write_len;
- size_t new_size;
-
- if (!len_w)
- return 0;
-
- assert(string_w);
-
- /* Measure the string necessary for conversion */
- if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string_w, len_w, NULL, 0, NULL, NULL)) == 0)
- return 0;
-
- assert(utf8_len > 0);
-
- GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, (size_t)utf8_len);
- GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
-
- if (git_buf_grow(buf, new_size) < 0)
- return -1;
-
- if ((utf8_write_len = WideCharToMultiByte(
- CP_UTF8, WC_ERR_INVALID_CHARS, string_w, len_w, &buf->ptr[buf->size], utf8_len, NULL, NULL)) == 0)
- return handle_wc_error();
-
- assert(utf8_write_len == utf8_len);
-
- buf->size += utf8_write_len;
- buf->ptr[buf->size] = '\0';
- return 0;
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_win32_buffer_h__
-#define INCLUDE_git_win32_buffer_h__
-
-#include "../buffer.h"
-
-/**
- * Convert a wide character string to UTF-8 and append the results to the
- * buffer.
- */
-int git_buf_put_w(git_buf *buf, const wchar_t *string_w, size_t len_w);
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#if defined(GIT_MSVC_CRTDBG)
-#include "w32_stack.h"
-#include "w32_crtdbg_stacktrace.h"
-
-#define CRTDBG_STACKTRACE__UID_LEN (15)
-
-/**
- * The stacktrace of an allocation can be distilled
- * to a unique id based upon the stackframe pointers
- * and ignoring any size arguments. We will use these
- * UIDs as the (char const*) __FILE__ argument we
- * give to the CRT malloc routines.
- */
-typedef struct {
- char uid[CRTDBG_STACKTRACE__UID_LEN + 1];
-} git_win32__crtdbg_stacktrace__uid;
-
-/**
- * All mallocs with the same stacktrace will be de-duped
- * and aggregated into this row.
- */
-typedef struct {
- git_win32__crtdbg_stacktrace__uid uid; /* must be first */
- git_win32__stack__raw_data raw_data;
- unsigned int count_allocs; /* times this alloc signature seen since init */
- unsigned int count_allocs_at_last_checkpoint; /* times since last mark */
- unsigned int transient_count_leaks; /* sum of leaks */
-} git_win32__crtdbg_stacktrace__row;
-
-static CRITICAL_SECTION g_crtdbg_stacktrace_cs;
-
-/**
- * CRTDBG memory leak tracking takes a "char const * const file_name"
- * and stores the pointer in the heap data (instead of allocing a copy
- * for itself). Normally, this is not a problem, since we usually pass
- * in __FILE__. But I'm going to lie to it and pass in the address of
- * the UID in place of the file_name. Also, I do not want to alloc the
- * stacktrace data (because we are called from inside our alloc routines).
- * Therefore, I'm creating a very large static pool array to store row
- * data. This also eliminates the temptation to realloc it (and move the
- * UID pointers).
- *
- * And to efficiently look for duplicates we need an index on the rows
- * so we can bsearch it. Again, without mallocing.
- *
- * If we observe more than MY_ROW_LIMIT unique malloc signatures, we
- * fall through and use the traditional __FILE__ processing and don't
- * try to de-dup them. If your testing hits this limit, just increase
- * it and try again.
- */
-
-#define MY_ROW_LIMIT (1024 * 1024)
-static git_win32__crtdbg_stacktrace__row g_cs_rows[MY_ROW_LIMIT];
-static git_win32__crtdbg_stacktrace__row *g_cs_index[MY_ROW_LIMIT];
-
-static unsigned int g_cs_end = MY_ROW_LIMIT;
-static unsigned int g_cs_ins = 0; /* insertion point == unique allocs seen */
-static unsigned int g_count_total_allocs = 0; /* number of allocs seen */
-static unsigned int g_transient_count_total_leaks = 0; /* number of total leaks */
-static unsigned int g_transient_count_dedup_leaks = 0; /* number of unique leaks */
-static bool g_limit_reached = false; /* had allocs after we filled row table */
-
-static unsigned int g_checkpoint_id = 0; /* to better label leak checkpoints */
-static bool g_transient_leaks_since_mark = false; /* payload for hook */
-
-/**
- * Compare function for bsearch on g_cs_index table.
- */
-static int row_cmp(const void *v1, const void *v2)
-{
- git_win32__stack__raw_data *d1 = (git_win32__stack__raw_data*)v1;
- git_win32__crtdbg_stacktrace__row *r2 = (git_win32__crtdbg_stacktrace__row *)v2;
-
- return (git_win32__stack_compare(d1, &r2->raw_data));
-}
-
-/**
- * Unique insert the new data into the row and index tables.
- * We have to sort by the stackframe data itself, not the uid.
- */
-static git_win32__crtdbg_stacktrace__row * insert_unique(
- const git_win32__stack__raw_data *pdata)
-{
- size_t pos;
- if (git__bsearch(g_cs_index, g_cs_ins, pdata, row_cmp, &pos) < 0) {
- /* Append new unique item to row table. */
- memcpy(&g_cs_rows[g_cs_ins].raw_data, pdata, sizeof(*pdata));
- sprintf(g_cs_rows[g_cs_ins].uid.uid, "##%08lx", g_cs_ins);
-
- /* Insert pointer to it into the proper place in the index table. */
- if (pos < g_cs_ins)
- memmove(&g_cs_index[pos+1], &g_cs_index[pos], (g_cs_ins - pos)*sizeof(g_cs_index[0]));
- g_cs_index[pos] = &g_cs_rows[g_cs_ins];
-
- g_cs_ins++;
- }
-
- g_cs_index[pos]->count_allocs++;
-
- return g_cs_index[pos];
-}
-
-/**
- * Hook function to receive leak data from the CRT. (This includes
- * both "<file_name>:(<line_number>)" data, but also each of the
- * various headers and fields.
- *
- * Scan this for the special "##<pos>" UID forms that we substituted
- * for the "<file_name>". Map <pos> back to the row data and
- * increment its leak count.
- *
- * See https://msdn.microsoft.com/en-us/library/74kabxyx.aspx
- *
- * We suppress the actual crtdbg output.
- */
-static int __cdecl report_hook(int nRptType, char *szMsg, int *retVal)
-{
- static int hook_result = TRUE; /* FALSE to get stock dump; TRUE to suppress. */
- unsigned int pos;
-
- *retVal = 0; /* do not invoke debugger */
-
- if ((szMsg[0] != '#') || (szMsg[1] != '#'))
- return hook_result;
-
- if (sscanf(&szMsg[2], "%08lx", &pos) < 1)
- return hook_result;
- if (pos >= g_cs_ins)
- return hook_result;
-
- if (g_transient_leaks_since_mark) {
- if (g_cs_rows[pos].count_allocs == g_cs_rows[pos].count_allocs_at_last_checkpoint)
- return hook_result;
- }
-
- g_cs_rows[pos].transient_count_leaks++;
-
- if (g_cs_rows[pos].transient_count_leaks == 1)
- g_transient_count_dedup_leaks++;
-
- g_transient_count_total_leaks++;
-
- return hook_result;
-}
-
-/**
- * Write leak data to all of the various places we need.
- * We force the caller to sprintf() the message first
- * because we want to avoid fprintf() because it allocs.
- */
-static void my_output(const char *buf)
-{
- fwrite(buf, strlen(buf), 1, stderr);
- OutputDebugString(buf);
-}
-
-/**
- * For each row with leaks, dump a stacktrace for it.
- */
-static void dump_summary(const char *label)
-{
- unsigned int k;
- char buf[10 * 1024];
-
- if (g_transient_count_total_leaks == 0)
- return;
-
- fflush(stdout);
- fflush(stderr);
- my_output("\n");
-
- if (g_limit_reached) {
- sprintf(buf,
- "LEAK SUMMARY: de-dup row table[%d] filled. Increase MY_ROW_LIMIT.\n",
- MY_ROW_LIMIT);
- my_output(buf);
- }
-
- if (!label)
- label = "";
-
- if (g_transient_leaks_since_mark) {
- sprintf(buf, "LEAK CHECKPOINT %d: leaks %d unique %d: %s\n",
- g_checkpoint_id, g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
- my_output(buf);
- } else {
- sprintf(buf, "LEAK SUMMARY: TOTAL leaks %d de-duped %d: %s\n",
- g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
- my_output(buf);
- }
- my_output("\n");
-
- for (k = 0; k < g_cs_ins; k++) {
- if (g_cs_rows[k].transient_count_leaks > 0) {
- sprintf(buf, "LEAK: %s leaked %d of %d times:\n",
- g_cs_rows[k].uid.uid,
- g_cs_rows[k].transient_count_leaks,
- g_cs_rows[k].count_allocs);
- my_output(buf);
-
- if (git_win32__stack_format(
- buf, sizeof(buf), &g_cs_rows[k].raw_data,
- NULL, NULL) >= 0) {
- my_output(buf);
- }
-
- my_output("\n");
- }
- }
-
- fflush(stderr);
-}
-
-void git_win32__crtdbg_stacktrace_init(void)
-{
- InitializeCriticalSection(&g_crtdbg_stacktrace_cs);
-
- EnterCriticalSection(&g_crtdbg_stacktrace_cs);
-
- _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
-
- _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
- _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
- _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
-
- _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
- _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
- _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
-
- LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
-}
-
-int git_win32__crtdbg_stacktrace__dump(
- git_win32__crtdbg_stacktrace_options opt,
- const char *label)
-{
- _CRT_REPORT_HOOK old;
- unsigned int k;
- int r = 0;
-
-#define IS_BIT_SET(o,b) (((o) & (b)) != 0)
-
- bool b_set_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK);
- bool b_leaks_since_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK);
- bool b_leaks_total = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL);
- bool b_quiet = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__QUIET);
-
- if (b_leaks_since_mark && b_leaks_total) {
- giterr_set(GITERR_INVALID, "Cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL.");
- return GIT_ERROR;
- }
- if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) {
- giterr_set(GITERR_INVALID, "Nothing to do.");
- return GIT_ERROR;
- }
-
- EnterCriticalSection(&g_crtdbg_stacktrace_cs);
-
- if (b_leaks_since_mark || b_leaks_total) {
- /* All variables with "transient" in the name are per-dump counters
- * and reset before each dump. This lets us handle checkpoints.
- */
- g_transient_count_total_leaks = 0;
- g_transient_count_dedup_leaks = 0;
- for (k = 0; k < g_cs_ins; k++) {
- g_cs_rows[k].transient_count_leaks = 0;
- }
- }
-
- g_transient_leaks_since_mark = b_leaks_since_mark;
-
- old = _CrtSetReportHook(report_hook);
- _CrtDumpMemoryLeaks();
- _CrtSetReportHook(old);
-
- if (b_leaks_since_mark || b_leaks_total) {
- r = g_transient_count_dedup_leaks;
-
- if (!b_quiet)
- dump_summary(label);
- }
-
- if (b_set_mark) {
- for (k = 0; k < g_cs_ins; k++) {
- g_cs_rows[k].count_allocs_at_last_checkpoint = g_cs_rows[k].count_allocs;
- }
-
- g_checkpoint_id++;
- }
-
- LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
-
- return r;
-}
-
-void git_win32__crtdbg_stacktrace_cleanup(void)
-{
- /* At shutdown/cleanup, dump cummulative leak info
- * with everything since startup. This might generate
- * extra noise if the caller has been doing checkpoint
- * dumps, but it might also eliminate some false
- * positives for resources previously reported during
- * checkpoints.
- */
- git_win32__crtdbg_stacktrace__dump(
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL,
- "CLEANUP");
-
- DeleteCriticalSection(&g_crtdbg_stacktrace_cs);
-}
-
-const char *git_win32__crtdbg_stacktrace(int skip, const char *file)
-{
- git_win32__stack__raw_data new_data;
- git_win32__crtdbg_stacktrace__row *row;
- const char * result = file;
-
- if (git_win32__stack_capture(&new_data, skip+1) < 0)
- return result;
-
- EnterCriticalSection(&g_crtdbg_stacktrace_cs);
-
- if (g_cs_ins < g_cs_end) {
- row = insert_unique(&new_data);
- result = row->uid.uid;
- } else {
- g_limit_reached = true;
- }
-
- g_count_total_allocs++;
-
- LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
-
- return result;
-}
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_w32_crtdbg_stacktrace_h__
-#define INCLUDE_w32_crtdbg_stacktrace_h__
-
-#if defined(GIT_MSVC_CRTDBG)
-
-/**
- * Initialize our memory leak tracking and de-dup data structures.
- * This should ONLY be called by git_libgit2_init().
- */
-void git_win32__crtdbg_stacktrace_init(void);
-
-/**
- * Shutdown our memory leak tracking and dump summary data.
- * This should ONLY be called by git_libgit2_shutdown().
- *
- * We explicitly call _CrtDumpMemoryLeaks() during here so
- * that we can compute summary data for the leaks. We print
- * the stacktrace of each unique leak.
- *
- * This cleanup does not happen if the app calls exit()
- * without calling the libgit2 shutdown code.
- *
- * This info we print here is independent of any automatic
- * reporting during exit() caused by _CRTDBG_LEAK_CHECK_DF.
- * Set it in your app if you also want traditional reporting.
- */
-void git_win32__crtdbg_stacktrace_cleanup(void);
-
-/**
- * Checkpoint options.
- */
-typedef enum git_win32__crtdbg_stacktrace_options {
- /**
- * Set checkpoint marker.
- */
- GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK = (1 << 0),
-
- /**
- * Dump leaks since last checkpoint marker.
- * May not be combined with __LEAKS_TOTAL.
- *
- * Note that this may generate false positives for global TLS
- * error state and other global caches that aren't cleaned up
- * until the thread/process terminates. So when using this
- * around a region of interest, also check the final (at exit)
- * dump before digging into leaks reported here.
- */
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK = (1 << 1),
-
- /**
- * Dump leaks since init. May not be combined
- * with __LEAKS_SINCE_MARK.
- */
- GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL = (1 << 2),
-
- /**
- * Suppress printing during dumps.
- * Just return leak count.
- */
- GIT_WIN32__CRTDBG_STACKTRACE__QUIET = (1 << 3),
-
-} git_win32__crtdbg_stacktrace_options;
-
-/**
- * Checkpoint memory state and/or dump unique stack traces of
- * current memory leaks.
- *
- * @return number of unique leaks (relative to requested starting
- * point) or error.
- */
-GIT_EXTERN(int) git_win32__crtdbg_stacktrace__dump(
- git_win32__crtdbg_stacktrace_options opt,
- const char *label);
-
-/**
- * Construct stacktrace and append it to the global buffer.
- * Return pointer to start of this string. On any error or
- * lack of buffer space, just return the given file buffer
- * so it will behave as usual.
- *
- * This should ONLY be called by our internal memory allocations
- * routines.
- */
-const char *git_win32__crtdbg_stacktrace(int skip, const char *file);
-
-#endif
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#if defined(GIT_MSVC_CRTDBG)
-#include "Windows.h"
-#include "Dbghelp.h"
-#include "win32/posix.h"
-#include "w32_stack.h"
-#include "hash.h"
-
-/**
- * This is supposedly defined in WinBase.h (from Windows.h) but there were linker issues.
- */
-USHORT WINAPI RtlCaptureStackBackTrace(ULONG, ULONG, PVOID*, PULONG);
-
-static bool g_win32_stack_initialized = false;
-static HANDLE g_win32_stack_process = INVALID_HANDLE_VALUE;
-static git_win32__stack__aux_cb_alloc g_aux_cb_alloc = NULL;
-static git_win32__stack__aux_cb_lookup g_aux_cb_lookup = NULL;
-
-int git_win32__stack__set_aux_cb(
- git_win32__stack__aux_cb_alloc cb_alloc,
- git_win32__stack__aux_cb_lookup cb_lookup)
-{
- g_aux_cb_alloc = cb_alloc;
- g_aux_cb_lookup = cb_lookup;
-
- return 0;
-}
-
-void git_win32__stack_init(void)
-{
- if (!g_win32_stack_initialized) {
- g_win32_stack_process = GetCurrentProcess();
- SymSetOptions(SYMOPT_LOAD_LINES);
- SymInitialize(g_win32_stack_process, NULL, TRUE);
- g_win32_stack_initialized = true;
- }
-}
-
-void git_win32__stack_cleanup(void)
-{
- if (g_win32_stack_initialized) {
- SymCleanup(g_win32_stack_process);
- g_win32_stack_process = INVALID_HANDLE_VALUE;
- g_win32_stack_initialized = false;
- }
-}
-
-int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip)
-{
- if (!g_win32_stack_initialized) {
- giterr_set(GITERR_INVALID, "git_win32_stack not initialized.");
- return GIT_ERROR;
- }
-
- memset(pdata, 0, sizeof(*pdata));
- pdata->nr_frames = RtlCaptureStackBackTrace(
- skip+1, GIT_WIN32__STACK__MAX_FRAMES, pdata->frames, NULL);
-
- /* If an "aux" data provider was registered, ask it to capture
- * whatever data it needs and give us an "aux_id" to it so that
- * we can refer to it later when reporting.
- */
- if (g_aux_cb_alloc)
- (g_aux_cb_alloc)(&pdata->aux_id);
-
- return 0;
-}
-
-int git_win32__stack_compare(
- git_win32__stack__raw_data *d1,
- git_win32__stack__raw_data *d2)
-{
- return memcmp(d1, d2, sizeof(*d1));
-}
-
-int git_win32__stack_format(
- char *pbuf, int buf_len,
- const git_win32__stack__raw_data *pdata,
- const char *prefix, const char *suffix)
-{
-#define MY_MAX_FILENAME 255
-
- /* SYMBOL_INFO has char FileName[1] at the end. The docs say to
- * to malloc it with extra space for your desired max filename.
- */
- struct {
- SYMBOL_INFO symbol;
- char extra[MY_MAX_FILENAME + 1];
- } s;
-
- IMAGEHLP_LINE64 line;
- int buf_used = 0;
- unsigned int k;
- char detail[MY_MAX_FILENAME * 2]; /* filename plus space for function name and formatting */
- int detail_len;
-
- if (!g_win32_stack_initialized) {
- giterr_set(GITERR_INVALID, "git_win32_stack not initialized.");
- return GIT_ERROR;
- }
-
- if (!prefix)
- prefix = "\t";
- if (!suffix)
- suffix = "\n";
-
- memset(pbuf, 0, buf_len);
-
- memset(&s, 0, sizeof(s));
- s.symbol.MaxNameLen = MY_MAX_FILENAME;
- s.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
-
- memset(&line, 0, sizeof(line));
- line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
-
- for (k=0; k < pdata->nr_frames; k++) {
- DWORD64 frame_k = (DWORD64)pdata->frames[k];
- DWORD dwUnused;
-
- if (SymFromAddr(g_win32_stack_process, frame_k, 0, &s.symbol) &&
- SymGetLineFromAddr64(g_win32_stack_process, frame_k, &dwUnused, &line)) {
- const char *pslash;
- const char *pfile;
-
- pslash = strrchr(line.FileName, '\\');
- pfile = ((pslash) ? (pslash+1) : line.FileName);
- p_snprintf(detail, sizeof(detail), "%s%s:%d> %s%s",
- prefix, pfile, line.LineNumber, s.symbol.Name, suffix);
- } else {
- /* This happens when we cross into another module.
- * For example, in CLAR tests, this is typically
- * the CRT startup code. Just print an unknown
- * frame and continue.
- */
- p_snprintf(detail, sizeof(detail), "%s??%s", prefix, suffix);
- }
- detail_len = strlen(detail);
-
- if (buf_len < (buf_used + detail_len + 1)) {
- /* we don't have room for this frame in the buffer, so just stop. */
- break;
- }
-
- memcpy(&pbuf[buf_used], detail, detail_len);
- buf_used += detail_len;
- }
-
- /* "aux_id" 0 is reserved to mean no aux data. This is needed to handle
- * allocs that occur before the aux callbacks were registered.
- */
- if (pdata->aux_id > 0) {
- p_snprintf(detail, sizeof(detail), "%saux_id: %d%s",
- prefix, pdata->aux_id, suffix);
- detail_len = strlen(detail);
- if ((buf_used + detail_len + 1) < buf_len) {
- memcpy(&pbuf[buf_used], detail, detail_len);
- buf_used += detail_len;
- }
-
- /* If an "aux" data provider is still registered, ask it to append its detailed
- * data to the end of ours using the "aux_id" it gave us when this de-duped
- * item was created.
- */
- if (g_aux_cb_lookup)
- (g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1));
- }
-
- return GIT_OK;
-}
-
-int git_win32__stack(
- char * pbuf, int buf_len,
- int skip,
- const char *prefix, const char *suffix)
-{
- git_win32__stack__raw_data data;
- int error;
-
- if ((error = git_win32__stack_capture(&data, skip)) < 0)
- return error;
- if ((error = git_win32__stack_format(pbuf, buf_len, &data, prefix, suffix)) < 0)
- return error;
- return 0;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_w32_stack_h__
-#define INCLUDE_w32_stack_h__
-
-#if defined(GIT_MSVC_CRTDBG)
-
-/**
- * This type defines a callback to be used to augment a C stacktrace
- * with "aux" data. This can be used, for example, to allow LibGit2Sharp
- * (or other interpreted consumer libraries) to give us C# stacktrace
- * data for the PInvoke.
- *
- * This callback will be called during crtdbg-instrumented allocs.
- *
- * @param aux_id [out] A returned "aux_id" representing a unique
- * (de-duped at the C# layer) stacktrace. "aux_id" 0 is reserved
- * to mean no aux stacktrace data.
- */
-typedef void (*git_win32__stack__aux_cb_alloc)(unsigned int *aux_id);
-
-/**
- * This type defines a callback to be used to augment the output of
- * a stacktrace. This will be used to request the C# layer format
- * the C# stacktrace associated with "aux_id" into the provided
- * buffer.
- *
- * This callback will be called during leak reporting.
- *
- * @param aux_id The "aux_id" key associated with a stacktrace.
- * @param aux_msg A buffer where a formatted message should be written.
- * @param aux_msg_len The size of the buffer.
- */
-typedef void (*git_win32__stack__aux_cb_lookup)(unsigned int aux_id, char *aux_msg, unsigned int aux_msg_len);
-
-/**
- * Register an "aux" data provider to augment our C stacktrace data.
- *
- * This can be used, for example, to allow LibGit2Sharp (or other
- * interpreted consumer libraries) to give us the C# stacktrace of
- * the PInvoke.
- *
- * If you choose to use this feature, it should be registered during
- * initialization and not changed for the duration of the process.
- */
-GIT_EXTERN(int) git_win32__stack__set_aux_cb(
- git_win32__stack__aux_cb_alloc cb_alloc,
- git_win32__stack__aux_cb_lookup cb_lookup);
-
-/**
- * Maximum number of stackframes to record for a
- * single stacktrace.
- */
-#define GIT_WIN32__STACK__MAX_FRAMES 30
-
-/**
- * Wrapper containing the raw unprocessed stackframe
- * data for a single stacktrace and any "aux_id".
- *
- * I put the aux_id first so leaks will be sorted by it.
- * So, for example, if a specific callstack in C# leaks
- * a repo handle, all of the pointers within the associated
- * repo pointer will be grouped together.
- */
-typedef struct {
- unsigned int aux_id;
- unsigned int nr_frames;
- void *frames[GIT_WIN32__STACK__MAX_FRAMES];
-} git_win32__stack__raw_data;
-
-
-/**
- * Load symbol table data. This should be done in the primary
- * thread at startup (under a lock if there are other threads
- * active).
- */
-void git_win32__stack_init(void);
-
-/**
- * Cleanup symbol table data. This should be done in the
- * primary thead at shutdown (under a lock if there are other
- * threads active).
- */
-void git_win32__stack_cleanup(void);
-
-
-/**
- * Capture raw stack trace data for the current process/thread.
- *
- * @param skip Number of initial frames to skip. Pass 0 to
- * begin with the caller of this routine. Pass 1 to begin
- * with its caller. And so on.
- */
-int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip);
-
-/**
- * Compare 2 raw stacktraces with the usual -1,0,+1 result.
- * This includes any "aux_id" values in the comparison, so that
- * our de-dup is also "aux" context relative.
- */
-int git_win32__stack_compare(
- git_win32__stack__raw_data *d1,
- git_win32__stack__raw_data *d2);
-
-/**
- * Format raw stacktrace data into buffer WITHOUT using any mallocs.
- *
- * @param prefix String written before each frame; defaults to "\t".
- * @param suffix String written after each frame; defaults to "\n".
- */
-int git_win32__stack_format(
- char *pbuf, int buf_len,
- const git_win32__stack__raw_data *pdata,
- const char *prefix, const char *suffix);
-
-/**
- * Convenience routine to capture and format stacktrace into
- * a buffer WITHOUT using any mallocs. This is primarily a
- * wrapper for testing.
- *
- * @param skip Number of initial frames to skip. Pass 0 to
- * begin with the caller of this routine. Pass 1 to begin
- * with its caller. And so on.
- * @param prefix String written before each frame; defaults to "\t".
- * @param suffix String written after each frame; defaults to "\n".
- */
-int git_win32__stack(
- char * pbuf, int buf_len,
- int skip,
- const char *prefix, const char *suffix);
-
-#endif /* GIT_MSVC_CRTDBG */
-#endif /* INCLUDE_w32_stack_h__ */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "w32_util.h"
-
-/**
- * Creates a FindFirstFile(Ex) filter string from a UTF-8 path.
- * The filter string enumerates all items in the directory.
- *
- * @param dest The buffer to receive the filter string.
- * @param src The UTF-8 path of the directory to enumerate.
- * @return True if the filter string was created successfully; false otherwise
- */
-bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src)
-{
- static const wchar_t suffix[] = L"\\*";
- int len = git_win32_path_from_utf8(dest, src);
-
- /* Ensure the path was converted */
- if (len < 0)
- return false;
-
- /* Ensure that the path does not end with a trailing slash,
- * because we're about to add one. Don't rely our trim_end
- * helper, because we want to remove the backslash even for
- * drive letter paths, in this case. */
- if (len > 0 &&
- (dest[len - 1] == L'/' || dest[len - 1] == L'\\')) {
- dest[len - 1] = L'\0';
- len--;
- }
-
- /* Ensure we have enough room to add the suffix */
- if ((size_t)len >= GIT_WIN_PATH_UTF16 - CONST_STRLEN(suffix))
- return false;
-
- wcscat(dest, suffix);
- return true;
-}
-
-/**
- * Ensures the given path (file or folder) has the +H (hidden) attribute set.
- *
- * @param path The path which should receive the +H bit.
- * @return 0 on success; -1 on failure
- */
-int git_win32__set_hidden(const char *path, bool hidden)
-{
- git_win32_path buf;
- DWORD attrs, newattrs;
-
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
-
- attrs = GetFileAttributesW(buf);
-
- /* Ensure the path exists */
- if (attrs == INVALID_FILE_ATTRIBUTES)
- return -1;
-
- if (hidden)
- newattrs = attrs | FILE_ATTRIBUTE_HIDDEN;
- else
- newattrs = attrs & ~FILE_ATTRIBUTE_HIDDEN;
-
- if (attrs != newattrs && !SetFileAttributesW(buf, newattrs)) {
- giterr_set(GITERR_OS, "Failed to %s hidden bit for '%s'",
- hidden ? "set" : "unset", path);
- return -1;
- }
-
- return 0;
-}
-
-int git_win32__hidden(bool *out, const char *path)
-{
- git_win32_path buf;
- DWORD attrs;
-
- if (git_win32_path_from_utf8(buf, path) < 0)
- return -1;
-
- attrs = GetFileAttributesW(buf);
-
- /* Ensure the path exists */
- if (attrs == INVALID_FILE_ATTRIBUTES)
- return -1;
-
- *out = (attrs & FILE_ATTRIBUTE_HIDDEN) ? true : false;
- return 0;
-}
-
-/**
- * Removes any trailing backslashes from a path, except in the case of a drive
- * letter path (C:\, D:\, etc.). This function cannot fail.
- *
- * @param path The path which should be trimmed.
- * @return The length of the modified string (<= the input length)
- */
-size_t git_win32__path_trim_end(wchar_t *str, size_t len)
-{
- while (1) {
- if (!len || str[len - 1] != L'\\')
- break;
-
- /* Don't trim backslashes from drive letter paths, which
- * are 3 characters long and of the form C:\, D:\, etc. */
- if (len == 3 && git_win32__isalpha(str[0]) && str[1] == ':')
- break;
-
- len--;
- }
-
- str[len] = L'\0';
-
- return len;
-}
-
-/**
- * Removes any of the following namespace prefixes from a path,
- * if found: "\??\", "\\?\", "\\?\UNC\". This function cannot fail.
- *
- * @param path The path which should be converted.
- * @return The length of the modified string (<= the input length)
- */
-size_t git_win32__canonicalize_path(wchar_t *str, size_t len)
-{
- static const wchar_t dosdevices_prefix[] = L"\\\?\?\\";
- static const wchar_t nt_prefix[] = L"\\\\?\\";
- static const wchar_t unc_prefix[] = L"UNC\\";
- size_t to_advance = 0;
-
- /* "\??\" -- DOS Devices prefix */
- if (len >= CONST_STRLEN(dosdevices_prefix) &&
- !wcsncmp(str, dosdevices_prefix, CONST_STRLEN(dosdevices_prefix))) {
- to_advance += CONST_STRLEN(dosdevices_prefix);
- len -= CONST_STRLEN(dosdevices_prefix);
- }
- /* "\\?\" -- NT namespace prefix */
- else if (len >= CONST_STRLEN(nt_prefix) &&
- !wcsncmp(str, nt_prefix, CONST_STRLEN(nt_prefix))) {
- to_advance += CONST_STRLEN(nt_prefix);
- len -= CONST_STRLEN(nt_prefix);
- }
-
- /* "\??\UNC\", "\\?\UNC\" -- UNC prefix */
- if (to_advance && len >= CONST_STRLEN(unc_prefix) &&
- !wcsncmp(str + to_advance, unc_prefix, CONST_STRLEN(unc_prefix))) {
- to_advance += CONST_STRLEN(unc_prefix);
- len -= CONST_STRLEN(unc_prefix);
- }
-
- if (to_advance) {
- memmove(str, str + to_advance, len * sizeof(wchar_t));
- str[len] = L'\0';
- }
-
- return git_win32__path_trim_end(str, len);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_w32_util_h__
-#define INCLUDE_w32_util_h__
-
-#include "utf-conv.h"
-#include "posix.h"
-#include "path_w32.h"
-
-/*
-
-#include "common.h"
-#include "path.h"
-#include "path_w32.h"
-#include "utf-conv.h"
-#include "posix.h"
-#include "reparse.h"
-#include "dir.h"
-*/
-
-
-GIT_INLINE(bool) git_win32__isalpha(wchar_t c)
-{
- return ((c >= L'A' && c <= L'Z') || (c >= L'a' && c <= L'z'));
-}
-
-/**
- * Creates a FindFirstFile(Ex) filter string from a UTF-8 path.
- * The filter string enumerates all items in the directory.
- *
- * @param dest The buffer to receive the filter string.
- * @param src The UTF-8 path of the directory to enumerate.
- * @return True if the filter string was created successfully; false otherwise
- */
-bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src);
-
-/**
- * Ensures the given path (file or folder) has the +H (hidden) attribute set
- * or unset.
- *
- * @param path The path that should receive the +H bit.
- * @param hidden true to set +H, false to unset it
- * @return 0 on success; -1 on failure
- */
-extern int git_win32__set_hidden(const char *path, bool hidden);
-
-/**
- * Determines if the given file or folder has the hidden attribute set.
- * @param hidden pointer to store hidden value
- * @param path The path that should be queried for hiddenness.
- * @return 0 on success or an error code.
- */
-extern int git_win32__hidden(bool *hidden, const char *path);
-
-/**
- * Removes any trailing backslashes from a path, except in the case of a drive
- * letter path (C:\, D:\, etc.). This function cannot fail.
- *
- * @param path The path which should be trimmed.
- * @return The length of the modified string (<= the input length)
- */
-size_t git_win32__path_trim_end(wchar_t *str, size_t len);
-
-/**
- * Removes any of the following namespace prefixes from a path,
- * if found: "\??\", "\\?\", "\\?\UNC\". This function cannot fail.
- *
- * @param path The path which should be converted.
- * @return The length of the modified string (<= the input length)
- */
-size_t git_win32__canonicalize_path(wchar_t *str, size_t len);
-
-/**
- * Converts a FILETIME structure to a struct timespec.
- *
- * @param FILETIME A pointer to a FILETIME
- * @param ts A pointer to the timespec structure to fill in
- */
-GIT_INLINE(void) git_win32__filetime_to_timespec(
- const FILETIME *ft,
- struct timespec *ts)
-{
- long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
- winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
- ts->tv_sec = (time_t)(winTime / 10000000);
-#ifdef GIT_USE_NSEC
- ts->tv_nsec = (winTime % 10000000) * 100;
-#else
- ts->tv_nsec = 0;
-#endif
-}
-
-GIT_INLINE(void) git_win32__timeval_to_filetime(
- FILETIME *ft, const struct p_timeval tv)
-{
- long long ticks = (tv.tv_sec * 10000000LL) +
- (tv.tv_usec * 10LL) + 116444736000000000LL;
-
- ft->dwHighDateTime = ((ticks >> 32) & 0xffffffffLL);
- ft->dwLowDateTime = (ticks & 0xffffffffLL);
-}
-
-GIT_INLINE(void) git_win32__stat_init(
- struct stat *st,
- DWORD dwFileAttributes,
- DWORD nFileSizeHigh,
- DWORD nFileSizeLow,
- FILETIME ftCreationTime,
- FILETIME ftLastAccessTime,
- FILETIME ftLastWriteTime)
-{
- mode_t mode = S_IREAD;
-
- memset(st, 0, sizeof(struct stat));
-
- if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- mode |= S_IFDIR;
- else
- mode |= S_IFREG;
-
- if ((dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
- mode |= S_IWRITE;
-
- st->st_ino = 0;
- st->st_gid = 0;
- st->st_uid = 0;
- st->st_nlink = 1;
- st->st_mode = mode;
- st->st_size = ((git_off_t)nFileSizeHigh << 32) + nFileSizeLow;
- st->st_dev = _getdrive() - 1;
- st->st_rdev = st->st_dev;
- git_win32__filetime_to_timespec(&ftLastAccessTime, &(st->st_atim));
- git_win32__filetime_to_timespec(&ftLastWriteTime, &(st->st_mtim));
- git_win32__filetime_to_timespec(&ftCreationTime, &(st->st_ctim));
-}
-
-GIT_INLINE(void) git_win32__file_information_to_stat(
- struct stat *st,
- const BY_HANDLE_FILE_INFORMATION *fileinfo)
-{
- git_win32__stat_init(st,
- fileinfo->dwFileAttributes,
- fileinfo->nFileSizeHigh,
- fileinfo->nFileSizeLow,
- fileinfo->ftCreationTime,
- fileinfo->ftLastAccessTime,
- fileinfo->ftLastWriteTime);
-}
-
-GIT_INLINE(int) git_win32__file_attribute_to_stat(
- struct stat *st,
- const WIN32_FILE_ATTRIBUTE_DATA *attrdata,
- const wchar_t *path)
-{
- git_win32__stat_init(st,
- attrdata->dwFileAttributes,
- attrdata->nFileSizeHigh,
- attrdata->nFileSizeLow,
- attrdata->ftCreationTime,
- attrdata->ftLastAccessTime,
- attrdata->ftLastWriteTime);
-
- if (attrdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && path) {
- git_win32_path target;
-
- if (git_win32_path_readlink_w(target, path) >= 0) {
- st->st_mode = (st->st_mode & ~S_IFMT) | S_IFLNK;
-
- /* st_size gets the UTF-8 length of the target name, in bytes,
- * not counting the NULL terminator */
- if ((st->st_size = git__utf16_to_8(NULL, 0, target)) < 0) {
- giterr_set(GITERR_OS, "Could not convert reparse point name for '%ls'", path);
- return -1;
- }
- }
- }
-
- return 0;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_win32_compat__
-#define INCLUDE_win32_compat__
-
-#include <stdint.h>
-#include <time.h>
-#include <wchar.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-typedef long suseconds_t;
-
-struct p_timeval {
- time_t tv_sec;
- suseconds_t tv_usec;
-};
-
-struct p_timespec {
- time_t tv_sec;
- long tv_nsec;
-};
-
-#define timespec p_timespec
-
-struct p_stat {
- _dev_t st_dev;
- _ino_t st_ino;
- mode_t st_mode;
- short st_nlink;
- short st_uid;
- short st_gid;
- _dev_t st_rdev;
- __int64 st_size;
- struct timespec st_atim;
- struct timespec st_mtim;
- struct timespec st_ctim;
-#define st_atime st_atim.tv_sec
-#define st_mtime st_mtim.tv_sec
-#define st_ctime st_ctim.tv_sec
-#define st_atime_nsec st_atim.tv_nsec
-#define st_mtime_nsec st_mtim.tv_nsec
-#define st_ctime_nsec st_ctim.tv_nsec
-};
-
-#define stat p_stat
-
-#endif /* INCLUDE_win32_compat__ */
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#include "../util.h"
-
-#if !defined(XDIFF_H)
-#define XDIFF_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* #ifdef __cplusplus */
-
-
-#define XDF_NEED_MINIMAL (1 << 1)
-#define XDF_IGNORE_WHITESPACE (1 << 2)
-#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
-#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
-#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
-
-#define XDF_PATIENCE_DIFF (1 << 5)
-#define XDF_HISTOGRAM_DIFF (1 << 6)
-#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
-#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
-
-#define XDF_IGNORE_BLANK_LINES (1 << 7)
-
-#define XDL_EMIT_FUNCNAMES (1 << 0)
-#define XDL_EMIT_COMMON (1 << 1)
-#define XDL_EMIT_FUNCCONTEXT (1 << 2)
-
-#define XDL_MMB_READONLY (1 << 0)
-
-#define XDL_MMF_ATOMIC (1 << 0)
-
-#define XDL_BDOP_INS 1
-#define XDL_BDOP_CPY 2
-#define XDL_BDOP_INSB 3
-
-/* merge simplification levels */
-#define XDL_MERGE_MINIMAL 0
-#define XDL_MERGE_EAGER 1
-#define XDL_MERGE_ZEALOUS 2
-#define XDL_MERGE_ZEALOUS_ALNUM 3
-
-/* merge favor modes */
-#define XDL_MERGE_FAVOR_OURS 1
-#define XDL_MERGE_FAVOR_THEIRS 2
-#define XDL_MERGE_FAVOR_UNION 3
-
-/* merge output styles */
-#define XDL_MERGE_DIFF3 1
-
-typedef struct s_mmfile {
- char *ptr;
- size_t size;
-} mmfile_t;
-
-typedef struct s_mmbuffer {
- char *ptr;
- size_t size;
-} mmbuffer_t;
-
-typedef struct s_xpparam {
- unsigned long flags;
-} xpparam_t;
-
-typedef struct s_xdemitcb {
- void *priv;
- int (*outf)(void *, mmbuffer_t *, int);
-} xdemitcb_t;
-
-typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
-
-typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a,
- long start_b, long count_b,
- void *cb_data);
-
-typedef struct s_xdemitconf {
- long ctxlen;
- long interhunkctxlen;
- unsigned long flags;
- find_func_t find_func;
- void *find_func_priv;
- xdl_emit_hunk_consume_func_t hunk_func;
-} xdemitconf_t;
-
-typedef struct s_bdiffparam {
- long bsize;
-} bdiffparam_t;
-
-
-#define xdl_malloc(x) git__malloc(x)
-#define xdl_free(ptr) git__free(ptr)
-#define xdl_realloc(ptr,x) git__realloc(ptr,x)
-
-void *xdl_mmfile_first(mmfile_t *mmf, long *size);
-long xdl_mmfile_size(mmfile_t *mmf);
-
-int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
- xdemitconf_t const *xecfg, xdemitcb_t *ecb);
-
-typedef struct s_xmparam {
- xpparam_t xpp;
- int marker_size;
- int level;
- int favor;
- int style;
- const char *ancestor; /* label for orig */
- const char *file1; /* label for mf1 */
- const char *file2; /* label for mf2 */
-} xmparam_t;
-
-#define DEFAULT_CONFLICT_MARKER_SIZE 7
-
-int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
- xmparam_t const *xmp, mmbuffer_t *result);
-
-#ifdef __cplusplus
-}
-#endif /* #ifdef __cplusplus */
-
-#endif /* #if !defined(XDIFF_H) */
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#include "xinclude.h"
-#include "common.h"
-#include "integer.h"
-
-
-#define XDL_MAX_COST_MIN 256
-#define XDL_HEUR_MIN_COST 256
-#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)
-#define XDL_SNAKE_CNT 20
-#define XDL_K_HEUR 4
-
-
-
-typedef struct s_xdpsplit {
- long i1, i2;
- int min_lo, min_hi;
-} xdpsplit_t;
-
-
-
-
-static long xdl_split(unsigned long const *ha1, long off1, long lim1,
- unsigned long const *ha2, long off2, long lim2,
- long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
- xdalgoenv_t *xenv);
-static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2);
-
-
-
-
-
-/*
- * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
- * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both
- * the forward diagonal starting from (off1, off2) and the backward diagonal
- * starting from (lim1, lim2). If the K values on the same diagonal crosses
- * returns the furthest point of reach. We might end up having to expensive
- * cases using this algorithm is full, so a little bit of heuristic is needed
- * to cut the search and to return a suboptimal point.
- */
-static long xdl_split(unsigned long const *ha1, long off1, long lim1,
- unsigned long const *ha2, long off2, long lim2,
- long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
- xdalgoenv_t *xenv) {
- long dmin = off1 - lim2, dmax = lim1 - off2;
- long fmid = off1 - off2, bmid = lim1 - lim2;
- long odd = (fmid - bmid) & 1;
- long fmin = fmid, fmax = fmid;
- long bmin = bmid, bmax = bmid;
- long ec, d, i1, i2, prev1, best, dd, v, k;
-
- /*
- * Set initial diagonal values for both forward and backward path.
- */
- kvdf[fmid] = off1;
- kvdb[bmid] = lim1;
-
- for (ec = 1;; ec++) {
- int got_snake = 0;
-
- /*
- * We need to extent the diagonal "domain" by one. If the next
- * values exits the box boundaries we need to change it in the
- * opposite direction because (max - min) must be a power of two.
- * Also we initialize the external K value to -1 so that we can
- * avoid extra conditions check inside the core loop.
- */
- if (fmin > dmin)
- kvdf[--fmin - 1] = -1;
- else
- ++fmin;
- if (fmax < dmax)
- kvdf[++fmax + 1] = -1;
- else
- --fmax;
-
- for (d = fmax; d >= fmin; d -= 2) {
- if (kvdf[d - 1] >= kvdf[d + 1])
- i1 = kvdf[d - 1] + 1;
- else
- i1 = kvdf[d + 1];
- prev1 = i1;
- i2 = i1 - d;
- for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);
- if (i1 - prev1 > xenv->snake_cnt)
- got_snake = 1;
- kvdf[d] = i1;
- if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) {
- spl->i1 = i1;
- spl->i2 = i2;
- spl->min_lo = spl->min_hi = 1;
- return ec;
- }
- }
-
- /*
- * We need to extent the diagonal "domain" by one. If the next
- * values exits the box boundaries we need to change it in the
- * opposite direction because (max - min) must be a power of two.
- * Also we initialize the external K value to -1 so that we can
- * avoid extra conditions check inside the core loop.
- */
- if (bmin > dmin)
- kvdb[--bmin - 1] = XDL_LINE_MAX;
- else
- ++bmin;
- if (bmax < dmax)
- kvdb[++bmax + 1] = XDL_LINE_MAX;
- else
- --bmax;
-
- for (d = bmax; d >= bmin; d -= 2) {
- if (kvdb[d - 1] < kvdb[d + 1])
- i1 = kvdb[d - 1];
- else
- i1 = kvdb[d + 1] - 1;
- prev1 = i1;
- i2 = i1 - d;
- for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);
- if (prev1 - i1 > xenv->snake_cnt)
- got_snake = 1;
- kvdb[d] = i1;
- if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) {
- spl->i1 = i1;
- spl->i2 = i2;
- spl->min_lo = spl->min_hi = 1;
- return ec;
- }
- }
-
- if (need_min)
- continue;
-
- /*
- * If the edit cost is above the heuristic trigger and if
- * we got a good snake, we sample current diagonals to see
- * if some of the, have reached an "interesting" path. Our
- * measure is a function of the distance from the diagonal
- * corner (i1 + i2) penalized with the distance from the
- * mid diagonal itself. If this value is above the current
- * edit cost times a magic factor (XDL_K_HEUR) we consider
- * it interesting.
- */
- if (got_snake && ec > xenv->heur_min) {
- for (best = 0, d = fmax; d >= fmin; d -= 2) {
- dd = d > fmid ? d - fmid: fmid - d;
- i1 = kvdf[d];
- i2 = i1 - d;
- v = (i1 - off1) + (i2 - off2) - dd;
-
- if (v > XDL_K_HEUR * ec && v > best &&
- off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&
- off2 + xenv->snake_cnt <= i2 && i2 < lim2) {
- for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)
- if (k == xenv->snake_cnt) {
- best = v;
- spl->i1 = i1;
- spl->i2 = i2;
- break;
- }
- }
- }
- if (best > 0) {
- spl->min_lo = 1;
- spl->min_hi = 0;
- return ec;
- }
-
- for (best = 0, d = bmax; d >= bmin; d -= 2) {
- dd = d > bmid ? d - bmid: bmid - d;
- i1 = kvdb[d];
- i2 = i1 - d;
- v = (lim1 - i1) + (lim2 - i2) - dd;
-
- if (v > XDL_K_HEUR * ec && v > best &&
- off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&
- off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {
- for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)
- if (k == xenv->snake_cnt - 1) {
- best = v;
- spl->i1 = i1;
- spl->i2 = i2;
- break;
- }
- }
- }
- if (best > 0) {
- spl->min_lo = 0;
- spl->min_hi = 1;
- return ec;
- }
- }
-
- /*
- * Enough is enough. We spent too much time here and now we collect
- * the furthest reaching path using the (i1 + i2) measure.
- */
- if (ec >= xenv->mxcost) {
- long fbest, fbest1, bbest, bbest1;
-
- fbest = fbest1 = -1;
- for (d = fmax; d >= fmin; d -= 2) {
- i1 = XDL_MIN(kvdf[d], lim1);
- i2 = i1 - d;
- if (lim2 < i2)
- i1 = lim2 + d, i2 = lim2;
- if (fbest < i1 + i2) {
- fbest = i1 + i2;
- fbest1 = i1;
- }
- }
-
- bbest = bbest1 = XDL_LINE_MAX;
- for (d = bmax; d >= bmin; d -= 2) {
- i1 = XDL_MAX(off1, kvdb[d]);
- i2 = i1 - d;
- if (i2 < off2)
- i1 = off2 + d, i2 = off2;
- if (i1 + i2 < bbest) {
- bbest = i1 + i2;
- bbest1 = i1;
- }
- }
-
- if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) {
- spl->i1 = fbest1;
- spl->i2 = fbest - fbest1;
- spl->min_lo = 1;
- spl->min_hi = 0;
- } else {
- spl->i1 = bbest1;
- spl->i2 = bbest - bbest1;
- spl->min_lo = 0;
- spl->min_hi = 1;
- }
- return ec;
- }
- }
-}
-
-
-/*
- * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling
- * the box splitting function. Note that the real job (marking changed lines)
- * is done in the two boundary reaching checks.
- */
-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
- diffdata_t *dd2, long off2, long lim2,
- long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {
- unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;
-
- /*
- * Shrink the box by walking through each diagonal snake (SW and NE).
- */
- for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);
- for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);
-
- /*
- * If one dimension is empty, then all records on the other one must
- * be obviously changed.
- */
- if (off1 == lim1) {
- char *rchg2 = dd2->rchg;
- long *rindex2 = dd2->rindex;
-
- for (; off2 < lim2; off2++)
- rchg2[rindex2[off2]] = 1;
- } else if (off2 == lim2) {
- char *rchg1 = dd1->rchg;
- long *rindex1 = dd1->rindex;
-
- for (; off1 < lim1; off1++)
- rchg1[rindex1[off1]] = 1;
- } else {
- xdpsplit_t spl;
- spl.i1 = spl.i2 = 0;
-
- /*
- * Divide ...
- */
- if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,
- need_min, &spl, xenv) < 0) {
-
- return -1;
- }
-
- /*
- * ... et Impera.
- */
- if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,
- kvdf, kvdb, spl.min_lo, xenv) < 0 ||
- xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,
- kvdf, kvdb, spl.min_hi, xenv) < 0) {
-
- return -1;
- }
- }
-
- return 0;
-}
-
-
-int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
- xdfenv_t *xe) {
- size_t ndiags, allocsize;
- long *kvd, *kvdf, *kvdb;
- xdalgoenv_t xenv;
- diffdata_t dd1, dd2;
-
- if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF)
- return xdl_do_patience_diff(mf1, mf2, xpp, xe);
-
- if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
- return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
-
- if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
-
- return -1;
- }
-
- /*
- * Allocate and setup K vectors to be used by the differential algorithm.
- * One is to store the forward path and one to store the backward path.
- */
- GITERR_CHECK_ALLOC_ADD3(&ndiags, xe->xdf1.nreff, xe->xdf2.nreff, 3);
- GITERR_CHECK_ALLOC_MULTIPLY(&allocsize, ndiags, 2);
- GITERR_CHECK_ALLOC_ADD(&allocsize, allocsize, 2);
- GITERR_CHECK_ALLOC_MULTIPLY(&allocsize, allocsize, sizeof(long));
-
- if (!(kvd = (long *) xdl_malloc(allocsize))) {
- xdl_free_env(xe);
- return -1;
- }
- kvdf = kvd;
- kvdb = kvdf + ndiags;
- kvdf += xe->xdf2.nreff + 1;
- kvdb += xe->xdf2.nreff + 1;
-
- xenv.mxcost = xdl_bogosqrt(ndiags);
- if (xenv.mxcost < XDL_MAX_COST_MIN)
- xenv.mxcost = XDL_MAX_COST_MIN;
- xenv.snake_cnt = XDL_SNAKE_CNT;
- xenv.heur_min = XDL_HEUR_MIN_COST;
-
- dd1.nrec = xe->xdf1.nreff;
- dd1.ha = xe->xdf1.ha;
- dd1.rchg = xe->xdf1.rchg;
- dd1.rindex = xe->xdf1.rindex;
- dd2.nrec = xe->xdf2.nreff;
- dd2.ha = xe->xdf2.ha;
- dd2.rchg = xe->xdf2.rchg;
- dd2.rindex = xe->xdf2.rindex;
-
- if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,
- kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) {
-
- xdl_free(kvd);
- xdl_free_env(xe);
- return -1;
- }
-
- xdl_free(kvd);
-
- return 0;
-}
-
-
-static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) {
- xdchange_t *xch;
-
- if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t))))
- return NULL;
-
- xch->next = xscr;
- xch->i1 = i1;
- xch->i2 = i2;
- xch->chg1 = chg1;
- xch->chg2 = chg2;
- xch->ignore = 0;
-
- return xch;
-}
-
-
-int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
- long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
- char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
- xrecord_t **recs = xdf->recs;
-
- /*
- * This is the same of what GNU diff does. Move back and forward
- * change groups for a consistent and pretty diff output. This also
- * helps in finding joinable change groups and reduce the diff size.
- */
- for (ix = ixo = 0;;) {
- /*
- * Find the first changed line in the to-be-compacted file.
- * We need to keep track of both indexes, so if we find a
- * changed lines group on the other file, while scanning the
- * to-be-compacted file, we need to skip it properly. Note
- * that loops that are testing for changed lines on rchg* do
- * not need index bounding since the array is prepared with
- * a zero at position -1 and N.
- */
- for (; ix < nrec && !rchg[ix]; ix++)
- while (rchgo[ixo++]);
- if (ix == nrec)
- break;
-
- /*
- * Record the start of a changed-group in the to-be-compacted file
- * and find the end of it, on both to-be-compacted and other file
- * indexes (ix and ixo).
- */
- ixs = ix;
- for (ix++; rchg[ix]; ix++);
- for (; rchgo[ixo]; ixo++);
-
- do {
- grpsiz = ix - ixs;
-
- /*
- * If the line before the current change group, is equal to
- * the last line of the current change group, shift backward
- * the group.
- */
- while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
- xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {
- rchg[--ixs] = 1;
- rchg[--ix] = 0;
-
- /*
- * This change might have joined two change groups,
- * so we try to take this scenario in account by moving
- * the start index accordingly (and so the other-file
- * end-of-group index).
- */
- for (; rchg[ixs - 1]; ixs--);
- while (rchgo[--ixo]);
- }
-
- /*
- * Record the end-of-group position in case we are matched
- * with a group of changes in the other file (that is, the
- * change record before the end-of-group index in the other
- * file is set).
- */
- ixref = rchgo[ixo - 1] ? ix: nrec;
-
- /*
- * If the first line of the current change group, is equal to
- * the line next of the current change group, shift forward
- * the group.
- */
- while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
- xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {
- rchg[ixs++] = 0;
- rchg[ix++] = 1;
-
- /*
- * This change might have joined two change groups,
- * so we try to take this scenario in account by moving
- * the start index accordingly (and so the other-file
- * end-of-group index). Keep tracking the reference
- * index in case we are shifting together with a
- * corresponding group of changes in the other file.
- */
- for (; rchg[ix]; ix++);
- while (rchgo[++ixo])
- ixref = ix;
- }
- } while (grpsiz != ix - ixs);
-
- /*
- * Try to move back the possibly merged group of changes, to match
- * the recorded position in the other file.
- */
- while (ixref < ix) {
- rchg[--ixs] = 1;
- rchg[--ix] = 0;
- while (rchgo[--ixo]);
- }
- }
-
- return 0;
-}
-
-
-int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
- xdchange_t *cscr = NULL, *xch;
- char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
- long i1, i2, l1, l2;
-
- /*
- * Trivial. Collects "groups" of changes and creates an edit script.
- */
- for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)
- if (rchg1[i1 - 1] || rchg2[i2 - 1]) {
- for (l1 = i1; rchg1[i1 - 1]; i1--);
- for (l2 = i2; rchg2[i2 - 1]; i2--);
-
- if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {
- xdl_free_script(cscr);
- return -1;
- }
- cscr = xch;
- }
-
- *xscr = cscr;
-
- return 0;
-}
-
-
-void xdl_free_script(xdchange_t *xscr) {
- xdchange_t *xch;
-
- while ((xch = xscr) != NULL) {
- xscr = xscr->next;
- xdl_free(xch);
- }
-}
-
-static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
- xdemitconf_t const *xecfg)
-{
- xdchange_t *xch, *xche;
-
- (void)xe;
-
- for (xch = xscr; xch; xch = xche->next) {
- xche = xdl_get_hunk(&xch, xecfg);
- if (!xch)
- break;
- if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
- xch->i2, xche->i2 + xche->chg2 - xch->i2,
- ecb->priv) < 0)
- return -1;
- }
- return 0;
-}
-
-static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
-{
- xdchange_t *xch;
-
- for (xch = xscr; xch; xch = xch->next) {
- int ignore = 1;
- xrecord_t **rec;
- long i;
-
- rec = &xe->xdf1.recs[xch->i1];
- for (i = 0; i < xch->chg1 && ignore; i++)
- ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
-
- rec = &xe->xdf2.recs[xch->i2];
- for (i = 0; i < xch->chg2 && ignore; i++)
- ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
-
- xch->ignore = ignore;
- }
-}
-
-int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
- xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
- xdchange_t *xscr;
- xdfenv_t xe;
- emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff;
-
- if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
-
- return -1;
- }
- if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
- xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
- xdl_build_script(&xe, &xscr) < 0) {
-
- xdl_free_env(&xe);
- return -1;
- }
- if (xscr) {
- if (xpp->flags & XDF_IGNORE_BLANK_LINES)
- xdl_mark_ignorable(xscr, &xe, xpp->flags);
-
- if (ef(&xe, xscr, ecb, xecfg) < 0) {
-
- xdl_free_script(xscr);
- xdl_free_env(&xe);
- return -1;
- }
- xdl_free_script(xscr);
- }
- xdl_free_env(&xe);
-
- return 0;
-}
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#if !defined(XDIFFI_H)
-#define XDIFFI_H
-
-
-typedef struct s_diffdata {
- long nrec;
- unsigned long const *ha;
- long *rindex;
- char *rchg;
-} diffdata_t;
-
-typedef struct s_xdalgoenv {
- long mxcost;
- long snake_cnt;
- long heur_min;
-} xdalgoenv_t;
-
-typedef struct s_xdchange {
- struct s_xdchange *next;
- long i1, i2;
- long chg1, chg2;
- int ignore;
-} xdchange_t;
-
-
-
-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
- diffdata_t *dd2, long off2, long lim2,
- long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);
-int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
- xdfenv_t *xe);
-int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags);
-int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);
-void xdl_free_script(xdchange_t *xscr);
-int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
- xdemitconf_t const *xecfg);
-int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
- xdfenv_t *env);
-int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
- xdfenv_t *env);
-
-#endif /* #if !defined(XDIFFI_H) */
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#include "xinclude.h"
-
-
-
-
-static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec);
-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb);
-
-
-
-
-static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
-
- *rec = xdf->recs[ri]->ptr;
-
- return xdf->recs[ri]->size;
-}
-
-
-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {
- long size, psize = (long)strlen(pre);
- char const *rec;
-
- size = xdl_get_rec(xdf, ri, &rec);
- if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {
-
- return -1;
- }
-
- return 0;
-}
-
-
-/*
- * Starting at the passed change atom, find the latest change atom to be included
- * inside the differential hunk according to the specified configuration.
- * Also advance xscr if the first changes must be discarded.
- */
-xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
-{
- xdchange_t *xch, *xchp, *lxch;
- long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
- long max_ignorable = xecfg->ctxlen;
- unsigned long ignored = 0; /* number of ignored blank lines */
-
- /* remove ignorable changes that are too far before other changes */
- for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
- xch = xchp->next;
-
- if (xch == NULL ||
- xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
- *xscr = xch;
- }
-
- if (*xscr == NULL)
- return NULL;
-
- lxch = *xscr;
-
- for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
- long distance = xch->i1 - (xchp->i1 + xchp->chg1);
- if (distance > max_common)
- break;
-
- if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
- lxch = xch;
- ignored = 0;
- } else if (distance < max_ignorable && xch->ignore) {
- ignored += xch->chg2;
- } else if (lxch != xchp &&
- xch->i1 + ignored - (lxch->i1 + lxch->chg1) > (unsigned long)max_common) {
- break;
- } else if (!xch->ignore) {
- lxch = xch;
- ignored = 0;
- } else {
- ignored += xch->chg2;
- }
- }
-
- return lxch;
-}
-
-
-static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
-{
- (void)priv;
-
- if (len > 0 &&
- (isalpha((unsigned char)*rec) || /* identifier? */
- *rec == '_' || /* also identifier? */
- *rec == '$')) { /* identifiers from VMS and other esoterico */
- if (len > sz)
- len = sz;
- while (0 < len && isspace((unsigned char)rec[len - 1]))
- len--;
- memcpy(buf, rec, len);
- return len;
- }
- return -1;
-}
-
-static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
- xdemitconf_t const *xecfg) {
- xdfile_t *xdf = &xe->xdf2;
- const char *rchg = xdf->rchg;
- long ix;
-
- (void)xscr;
- (void)xecfg;
-
- for (ix = 0; ix < xdf->nrec; ix++) {
- if (rchg[ix])
- continue;
- if (xdl_emit_record(xdf, ix, "", ecb))
- return -1;
- }
- return 0;
-}
-
-struct func_line {
- long len;
- char buf[80];
-};
-
-static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
- struct func_line *func_line, long start, long limit)
-{
- find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
- long l, size, step = (start > limit) ? -1 : 1;
- char *buf, dummy[1];
-
- buf = func_line ? func_line->buf : dummy;
- size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
-
- for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
- const char *rec;
- long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
- long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);
- if (len >= 0) {
- if (func_line)
- func_line->len = len;
- return l;
- }
- }
- return -1;
-}
-
-int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
- xdemitconf_t const *xecfg) {
- long s1, s2, e1, e2, lctx;
- xdchange_t *xch, *xche;
- long funclineprev = -1;
- struct func_line func_line = { 0 };
-
- if (xecfg->flags & XDL_EMIT_COMMON)
- return xdl_emit_common(xe, xscr, ecb, xecfg);
-
- for (xch = xscr; xch; xch = xche->next) {
- xche = xdl_get_hunk(&xch, xecfg);
- if (!xch)
- break;
-
- s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
- s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
-
- if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
- long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);
- if (fs1 < 0)
- fs1 = 0;
- if (fs1 < s1) {
- s2 -= s1 - fs1;
- s1 = fs1;
- }
- }
-
- again:
- lctx = xecfg->ctxlen;
- lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
- lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
-
- e1 = xche->i1 + xche->chg1 + lctx;
- e2 = xche->i2 + xche->chg2 + lctx;
-
- if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
- long fe1 = get_func_line(xe, xecfg, NULL,
- xche->i1 + xche->chg1,
- xe->xdf1.nrec);
- if (fe1 < 0)
- fe1 = xe->xdf1.nrec;
- if (fe1 > e1) {
- e2 += fe1 - e1;
- e1 = fe1;
- }
-
- /*
- * Overlap with next change? Then include it
- * in the current hunk and start over to find
- * its new end.
- */
- if (xche->next) {
- long l = xche->next->i1;
- if (l <= e1 ||
- get_func_line(xe, xecfg, NULL, l, e1) < 0) {
- xche = xche->next;
- goto again;
- }
- }
- }
-
- /*
- * Emit current hunk header.
- */
-
- if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
- get_func_line(xe, xecfg, &func_line,
- s1 - 1, funclineprev);
- funclineprev = s1 - 1;
- }
- if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
- func_line.buf, func_line.len, ecb) < 0)
- return -1;
-
- /*
- * Emit pre-context.
- */
- for (; s2 < xch->i2; s2++)
- if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
- return -1;
-
- for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) {
- /*
- * Merge previous with current change atom.
- */
- for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++)
- if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
- return -1;
-
- /*
- * Removes lines from the first file.
- */
- for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++)
- if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0)
- return -1;
-
- /*
- * Adds lines from the second file.
- */
- for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++)
- if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0)
- return -1;
-
- if (xch == xche)
- break;
- s1 = xch->i1 + xch->chg1;
- s2 = xch->i2 + xch->chg2;
- }
-
- /*
- * Emit post-context.
- */
- for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++)
- if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
- return -1;
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#if !defined(XEMIT_H)
-#define XEMIT_H
-
-
-typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
- xdemitconf_t const *xecfg);
-
-xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);
-int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
- xdemitconf_t const *xecfg);
-
-
-
-#endif /* #if !defined(XEMIT_H) */
+++ /dev/null
-/*
- * Copyright (C) 2010, Google Inc.
- * and other copyright owners as documented in JGit's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "xinclude.h"
-#include "xtypes.h"
-#include "xdiff.h"
-#include "common.h"
-
-#define MAX_PTR UINT_MAX
-#define MAX_CNT UINT_MAX
-
-#define LINE_END(n) (line##n + count##n - 1)
-#define LINE_END_PTR(n) (*line##n + *count##n - 1)
-
-struct histindex {
- struct record {
- unsigned int ptr, cnt;
- struct record *next;
- } **records, /* an occurrence */
- **line_map; /* map of line to record chain */
- chastore_t rcha;
- unsigned int *next_ptrs;
- unsigned int table_bits,
- records_size,
- line_map_size;
-
- unsigned int max_chain_length,
- key_shift,
- ptr_shift;
-
- unsigned int cnt,
- has_common;
-
- xdfenv_t *env;
- xpparam_t const *xpp;
-};
-
-struct region {
- unsigned int begin1, end1;
- unsigned int begin2, end2;
-};
-
-#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift])
-
-#define NEXT_PTR(index, ptr) \
- (index->next_ptrs[(ptr) - index->ptr_shift])
-
-#define CNT(index, ptr) \
- ((LINE_MAP(index, ptr))->cnt)
-
-#define REC(env, s, l) \
- (env->xdf##s.recs[l - 1])
-
-static int cmp_recs(xpparam_t const *xpp,
- xrecord_t *r1, xrecord_t *r2)
-{
- return r1->ha == r2->ha &&
- xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size,
- xpp->flags);
-}
-
-#define CMP_ENV(xpp, env, s1, l1, s2, l2) \
- (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2)))
-
-#define CMP(i, s1, l1, s2, l2) \
- (cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2)))
-
-#define TABLE_HASH(index, side, line) \
- XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)
-
-static int scanA(struct histindex *index, unsigned int line1, unsigned int count1)
-{
- unsigned int ptr;
- unsigned int tbl_idx;
- unsigned int chain_len;
- struct record **rec_chain, *rec;
-
- for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
- tbl_idx = TABLE_HASH(index, 1, ptr);
- rec_chain = index->records + tbl_idx;
- rec = *rec_chain;
-
- chain_len = 0;
- while (rec) {
- if (CMP(index, 1, rec->ptr, 1, ptr)) {
- /*
- * ptr is identical to another element. Insert
- * it onto the front of the existing element
- * chain.
- */
- NEXT_PTR(index, ptr) = rec->ptr;
- rec->ptr = ptr;
- /* cap rec->cnt at MAX_CNT */
- rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1);
- LINE_MAP(index, ptr) = rec;
- goto continue_scan;
- }
-
- rec = rec->next;
- chain_len++;
- }
-
- if (chain_len == index->max_chain_length)
- return -1;
-
- /*
- * This is the first time we have ever seen this particular
- * element in the sequence. Construct a new chain for it.
- */
- if (!(rec = xdl_cha_alloc(&index->rcha)))
- return -1;
- rec->ptr = ptr;
- rec->cnt = 1;
- rec->next = *rec_chain;
- *rec_chain = rec;
- LINE_MAP(index, ptr) = rec;
-
-continue_scan:
- ; /* no op */
- }
-
- return 0;
-}
-
-static int try_lcs(
- struct histindex *index, struct region *lcs, unsigned int b_ptr,
- unsigned int line1, unsigned int count1,
- unsigned int line2, unsigned int count2)
-{
- unsigned int b_next = b_ptr + 1;
- struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)];
- unsigned int as, ae, bs, be, np, rc;
- int should_break;
-
- for (; rec; rec = rec->next) {
- if (rec->cnt > index->cnt) {
- if (!index->has_common)
- index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr);
- continue;
- }
-
- as = rec->ptr;
- if (!CMP(index, 1, as, 2, b_ptr))
- continue;
-
- index->has_common = 1;
- for (;;) {
- should_break = 0;
- np = NEXT_PTR(index, as);
- bs = b_ptr;
- ae = as;
- be = bs;
- rc = rec->cnt;
-
- while (line1 < as && line2 < bs
- && CMP(index, 1, as - 1, 2, bs - 1)) {
- as--;
- bs--;
- if (1 < rc)
- rc = XDL_MIN(rc, CNT(index, as));
- }
- while (ae < LINE_END(1) && be < LINE_END(2)
- && CMP(index, 1, ae + 1, 2, be + 1)) {
- ae++;
- be++;
- if (1 < rc)
- rc = XDL_MIN(rc, CNT(index, ae));
- }
-
- if (b_next <= be)
- b_next = be + 1;
- if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) {
- lcs->begin1 = as;
- lcs->begin2 = bs;
- lcs->end1 = ae;
- lcs->end2 = be;
- index->cnt = rc;
- }
-
- if (np == 0)
- break;
-
- while (np <= ae) {
- np = NEXT_PTR(index, np);
- if (np == 0) {
- should_break = 1;
- break;
- }
- }
-
- if (should_break)
- break;
-
- as = np;
- }
- }
- return b_next;
-}
-
-static int find_lcs(
- struct histindex *index, struct region *lcs,
- unsigned int line1, unsigned int count1,
- unsigned int line2, unsigned int count2)
-{
- unsigned int b_ptr;
-
- if (scanA(index, line1, count1))
- return -1;
-
- index->cnt = index->max_chain_length + 1;
-
- for (b_ptr = line2; b_ptr <= LINE_END(2); )
- b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2);
-
- return index->has_common && index->max_chain_length < index->cnt;
-}
-
-static int fall_back_to_classic_diff(struct histindex *index,
- int line1, int count1, int line2, int count2)
-{
- xpparam_t xpp;
- xpp.flags = index->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
-
- return xdl_fall_back_diff(index->env, &xpp,
- line1, count1, line2, count2);
-}
-
-static int histogram_diff(
- xpparam_t const *xpp, xdfenv_t *env,
- unsigned int line1, unsigned int count1,
- unsigned int line2, unsigned int count2)
-{
- struct histindex index;
- struct region lcs;
- size_t sz;
- int result = -1;
-
- if (count1 <= 0 && count2 <= 0)
- return 0;
-
- if (LINE_END(1) >= MAX_PTR)
- return -1;
-
- if (!count1) {
- while(count2--)
- env->xdf2.rchg[line2++ - 1] = 1;
- return 0;
- } else if (!count2) {
- while(count1--)
- env->xdf1.rchg[line1++ - 1] = 1;
- return 0;
- }
-
- memset(&index, 0, sizeof(index));
-
- index.env = env;
- index.xpp = xpp;
-
- index.records = NULL;
- index.line_map = NULL;
- /* in case of early xdl_cha_free() */
- index.rcha.head = NULL;
-
- index.table_bits = xdl_hashbits(count1);
- sz = index.records_size = 1 << index.table_bits;
- GITERR_CHECK_ALLOC_MULTIPLY(&sz, sz, sizeof(struct record *));
-
- if (!(index.records = (struct record **) xdl_malloc(sz)))
- goto cleanup;
- memset(index.records, 0, sz);
-
- sz = index.line_map_size = count1;
- sz *= sizeof(struct record *);
- if (!(index.line_map = (struct record **) xdl_malloc(sz)))
- goto cleanup;
- memset(index.line_map, 0, sz);
-
- sz = index.line_map_size;
- sz *= sizeof(unsigned int);
- if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz)))
- goto cleanup;
- memset(index.next_ptrs, 0, sz);
-
- /* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */
- if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)
- goto cleanup;
-
- index.ptr_shift = line1;
- index.max_chain_length = 64;
-
- memset(&lcs, 0, sizeof(lcs));
- if (find_lcs(&index, &lcs, line1, count1, line2, count2))
- result = fall_back_to_classic_diff(&index, line1, count1, line2, count2);
- else {
- if (lcs.begin1 == 0 && lcs.begin2 == 0) {
- while (count1--)
- env->xdf1.rchg[line1++ - 1] = 1;
- while (count2--)
- env->xdf2.rchg[line2++ - 1] = 1;
- result = 0;
- } else {
- result = histogram_diff(xpp, env,
- line1, lcs.begin1 - line1,
- line2, lcs.begin2 - line2);
- if (result)
- goto cleanup;
- result = histogram_diff(xpp, env,
- lcs.end1 + 1, LINE_END(1) - lcs.end1,
- lcs.end2 + 1, LINE_END(2) - lcs.end2);
- if (result)
- goto cleanup;
- }
- }
-
-cleanup:
- xdl_free(index.records);
- xdl_free(index.line_map);
- xdl_free(index.next_ptrs);
- xdl_cha_free(&index.rcha);
-
- return result;
-}
-
-int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2,
- xpparam_t const *xpp, xdfenv_t *env)
-{
- if (xdl_prepare_env(file1, file2, xpp, env) < 0)
- return -1;
-
- return histogram_diff(xpp, env,
- env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
- env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
-}
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#if !defined(XINCLUDE_H)
-#define XINCLUDE_H
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#ifdef _WIN32
-#else
-#include <unistd.h>
-#endif
-
-#include "xmacros.h"
-#include "xdiff.h"
-#include "xtypes.h"
-#include "xutils.h"
-#include "xprepare.h"
-#include "xdiffi.h"
-#include "xemit.h"
-
-
-#endif /* #if !defined(XINCLUDE_H) */
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#if !defined(XMACROS_H)
-#define XMACROS_H
-
-
-
-
-#define XDL_MIN(a, b) ((a) < (b) ? (a): (b))
-#define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
-#define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
-#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
-#define XDL_ISSPACE(c) (isspace((unsigned char)(c)))
-#define XDL_ADDBITS(v,b) ((v) + ((v) >> (b)))
-#define XDL_MASKBITS(b) ((1UL << (b)) - 1)
-#define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
-#define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0)
-#define XDL_LE32_PUT(p, v) \
-do { \
- unsigned char *__p = (unsigned char *) (p); \
- *__p++ = (unsigned char) (v); \
- *__p++ = (unsigned char) ((v) >> 8); \
- *__p++ = (unsigned char) ((v) >> 16); \
- *__p = (unsigned char) ((v) >> 24); \
-} while (0)
-#define XDL_LE32_GET(p, v) \
-do { \
- unsigned char const *__p = (unsigned char const *) (p); \
- (v) = (unsigned long) __p[0] | ((unsigned long) __p[1]) << 8 | \
- ((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \
-} while (0)
-
-
-#endif /* #if !defined(XMACROS_H) */
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003-2006 Davide Libenzi, Johannes E. Schindelin
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#include "xinclude.h"
-#include "common.h"
-
-typedef struct s_xdmerge {
- struct s_xdmerge *next;
- /*
- * 0 = conflict,
- * 1 = no conflict, take first,
- * 2 = no conflict, take second.
- * 3 = no conflict, take both.
- */
- int mode;
- /*
- * These point at the respective postimages. E.g. <i1,chg1> is
- * how side #1 wants to change the common ancestor; if there is no
- * overlap, lines before i1 in the postimage of side #1 appear
- * in the merge result as a region touched by neither side.
- */
- long i1, i2;
- long chg1, chg2;
- /*
- * These point at the preimage; of course there is just one
- * preimage, that is from the shared common ancestor.
- */
- long i0;
- long chg0;
-} xdmerge_t;
-
-static int xdl_append_merge(xdmerge_t **merge, int mode,
- long i0, long chg0,
- long i1, long chg1,
- long i2, long chg2)
-{
- xdmerge_t *m = *merge;
- if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
- if (mode != m->mode)
- m->mode = 0;
- m->chg0 = i0 + chg0 - m->i0;
- m->chg1 = i1 + chg1 - m->i1;
- m->chg2 = i2 + chg2 - m->i2;
- } else {
- m = xdl_malloc(sizeof(xdmerge_t));
- if (!m)
- return -1;
- m->next = NULL;
- m->mode = mode;
- m->i0 = i0;
- m->chg0 = chg0;
- m->i1 = i1;
- m->chg1 = chg1;
- m->i2 = i2;
- m->chg2 = chg2;
- if (*merge)
- (*merge)->next = m;
- *merge = m;
- }
- return 0;
-}
-
-static int xdl_cleanup_merge(xdmerge_t *c)
-{
- int count = 0;
- xdmerge_t *next_c;
-
- /* were there conflicts? */
- for (; c; c = next_c) {
- if (c->mode == 0)
- count++;
- next_c = c->next;
- free(c);
- }
- return count;
-}
-
-static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
- int line_count, long flags)
-{
- int i;
- xrecord_t **rec1 = xe1->xdf2.recs + i1;
- xrecord_t **rec2 = xe2->xdf2.recs + i2;
-
- for (i = 0; i < line_count; i++) {
- int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,
- rec2[i]->ptr, rec2[i]->size, flags);
- if (!result)
- return -1;
- }
- return 0;
-}
-
-static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
-{
- xrecord_t **recs;
- size_t size = 0;
-
- *out = 0;
-
- recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
-
- if (count < 1)
- return 0;
-
- for (i = 0; i < count; ) {
- if (dest)
- memcpy(dest + size, recs[i]->ptr, recs[i]->size);
-
- GITERR_CHECK_ALLOC_ADD(&size, size, recs[i++]->size);
- }
-
- if (add_nl) {
- i = recs[count - 1]->size;
- if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') {
- if (dest)
- dest[size] = '\n';
-
- GITERR_CHECK_ALLOC_ADD(&size, size, 1);
- }
- }
-
- *out = size;
- return 0;
-}
-
-static int xdl_recs_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
-{
- return xdl_recs_copy_0(out, 0, xe, i, count, add_nl, dest);
-}
-
-static int xdl_orig_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
-{
- return xdl_recs_copy_0(out, 1, xe, i, count, add_nl, dest);
-}
-
-static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1,
- xdfenv_t *xe2, const char *name2,
- const char *name3,
- size_t size, int i, int style,
- xdmerge_t *m, char *dest, int marker_size)
-{
- int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0);
- int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0);
- int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0);
- size_t copied;
-
- *out = 0;
-
- if (marker_size <= 0)
- marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
-
- /* Before conflicting part */
- if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0,
- dest ? dest + size : NULL) < 0)
- return -1;
-
- GITERR_CHECK_ALLOC_ADD(&size, size, copied);
-
- if (!dest) {
- GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker1_size);
- } else {
- memset(dest + size, '<', marker_size);
- size += marker_size;
- if (marker1_size) {
- dest[size] = ' ';
- memcpy(dest + size + 1, name1, marker1_size - 1);
- size += marker1_size;
- }
- dest[size++] = '\n';
- }
-
- /* Postimage from side #1 */
- if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, 1,
- dest ? dest + size : NULL) < 0)
- return -1;
-
- GITERR_CHECK_ALLOC_ADD(&size, size, copied);
-
- if (style == XDL_MERGE_DIFF3) {
- /* Shared preimage */
- if (!dest) {
- GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker3_size);
- } else {
- memset(dest + size, '|', marker_size);
- size += marker_size;
- if (marker3_size) {
- dest[size] = ' ';
- memcpy(dest + size + 1, name3, marker3_size - 1);
- size += marker3_size;
- }
- dest[size++] = '\n';
- }
-
- if (xdl_orig_copy(&copied, xe1, m->i0, m->chg0, 1,
- dest ? dest + size : NULL) < 0)
- return -1;
- GITERR_CHECK_ALLOC_ADD(&size, size, copied);
- }
-
- if (!dest) {
- GITERR_CHECK_ALLOC_ADD3(&size, size, marker_size, 1);
- } else {
- memset(dest + size, '=', marker_size);
- size += marker_size;
- dest[size++] = '\n';
- }
-
- /* Postimage from side #2 */
-
- if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 1,
- dest ? dest + size : NULL) < 0)
- return -1;
- GITERR_CHECK_ALLOC_ADD(&size, size, copied);
-
- if (!dest) {
- GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker2_size);
- } else {
- memset(dest + size, '>', marker_size);
- size += marker_size;
- if (marker2_size) {
- dest[size] = ' ';
- memcpy(dest + size + 1, name2, marker2_size - 1);
- size += marker2_size;
- }
- dest[size++] = '\n';
- }
-
- *out = size;
- return 0;
-}
-
-static int xdl_fill_merge_buffer(size_t *out,
- xdfenv_t *xe1, const char *name1,
- xdfenv_t *xe2, const char *name2,
- const char *ancestor_name,
- int favor,
- xdmerge_t *m, char *dest, int style,
- int marker_size)
-{
- size_t size, copied;
- int i;
-
- *out = 0;
-
- for (size = i = 0; m; m = m->next) {
- if (favor && !m->mode)
- m->mode = favor;
-
- if (m->mode == 0) {
- if (fill_conflict_hunk(&size, xe1, name1, xe2, name2,
- ancestor_name,
- size, i, style, m, dest,
- marker_size) < 0)
- return -1;
- }
- else if (m->mode & 3) {
- /* Before conflicting part */
- if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0,
- dest ? dest + size : NULL) < 0)
- return -1;
- GITERR_CHECK_ALLOC_ADD(&size, size, copied);
-
- /* Postimage from side #1 */
- if (m->mode & 1) {
- if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, (m->mode & 2),
- dest ? dest + size : NULL) < 0)
- return -1;
- GITERR_CHECK_ALLOC_ADD(&size, size, copied);
- }
-
- /* Postimage from side #2 */
- if (m->mode & 2) {
- if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 0,
- dest ? dest + size : NULL) < 0)
- return -1;
- GITERR_CHECK_ALLOC_ADD(&size, size, copied);
- }
- } else
- continue;
- i = m->i1 + m->chg1;
- }
-
- if (xdl_recs_copy(&copied, xe1, i, xe1->xdf2.nrec - i, 0,
- dest ? dest + size : NULL) < 0)
- return -1;
- GITERR_CHECK_ALLOC_ADD(&size, size, copied);
-
- *out = size;
- return 0;
-}
-
-/*
- * Sometimes, changes are not quite identical, but differ in only a few
- * lines. Try hard to show only these few lines as conflicting.
- */
-static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
- xpparam_t const *xpp)
-{
- for (; m; m = m->next) {
- mmfile_t t1, t2;
- xdfenv_t xe;
- xdchange_t *xscr, *x;
- int i1 = m->i1, i2 = m->i2;
-
- /* let's handle just the conflicts */
- if (m->mode)
- continue;
-
- /* no sense refining a conflict when one side is empty */
- if (m->chg1 == 0 || m->chg2 == 0)
- continue;
-
- /*
- * This probably does not work outside git, since
- * we have a very simple mmfile structure.
- */
- t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;
- t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr
- + xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;
- t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;
- t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr
- + xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;
- if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)
- return -1;
- if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
- xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
- xdl_build_script(&xe, &xscr) < 0) {
- xdl_free_env(&xe);
- return -1;
- }
- if (!xscr) {
- /* If this happens, the changes are identical. */
- xdl_free_env(&xe);
- m->mode = 4;
- continue;
- }
- x = xscr;
- m->i1 = xscr->i1 + i1;
- m->chg1 = xscr->chg1;
- m->i2 = xscr->i2 + i2;
- m->chg2 = xscr->chg2;
- while (xscr->next) {
- xdmerge_t *m2 = xdl_malloc(sizeof(xdmerge_t));
- if (!m2) {
- xdl_free_env(&xe);
- xdl_free_script(x);
- return -1;
- }
- xscr = xscr->next;
- m2->next = m->next;
- m->next = m2;
- m = m2;
- m->mode = 0;
- m->i1 = xscr->i1 + i1;
- m->chg1 = xscr->chg1;
- m->i2 = xscr->i2 + i2;
- m->chg2 = xscr->chg2;
- }
- xdl_free_env(&xe);
- xdl_free_script(x);
- }
- return 0;
-}
-
-static int line_contains_alnum(const char *ptr, long size)
-{
- while (size--)
- if (isalnum((unsigned char)*(ptr++)))
- return 1;
- return 0;
-}
-
-static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)
-{
- for (; chg; chg--, i++)
- if (line_contains_alnum(xe->xdf2.recs[i]->ptr,
- xe->xdf2.recs[i]->size))
- return 1;
- return 0;
-}
-
-/*
- * This function merges m and m->next, marking everything between those hunks
- * as conflicting, too.
- */
-static void xdl_merge_two_conflicts(xdmerge_t *m)
-{
- xdmerge_t *next_m = m->next;
- m->chg1 = next_m->i1 + next_m->chg1 - m->i1;
- m->chg2 = next_m->i2 + next_m->chg2 - m->i2;
- m->next = next_m->next;
- free(next_m);
-}
-
-/*
- * If there are less than 3 non-conflicting lines between conflicts,
- * it appears simpler -- because it takes up less (or as many) lines --
- * if the lines are moved into the conflicts.
- */
-static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
- int simplify_if_no_alnum)
-{
- int result = 0;
-
- if (!m)
- return result;
- for (;;) {
- xdmerge_t *next_m = m->next;
- int begin, end;
-
- if (!next_m)
- return result;
-
- begin = m->i1 + m->chg1;
- end = next_m->i1;
-
- if (m->mode != 0 || next_m->mode != 0 ||
- (end - begin > 3 &&
- (!simplify_if_no_alnum ||
- lines_contain_alnum(xe1, begin, end - begin)))) {
- m = next_m;
- } else {
- result++;
- xdl_merge_two_conflicts(m);
- }
- }
-}
-
-/*
- * level == 0: mark all overlapping changes as conflict
- * level == 1: mark overlapping changes as conflict only if not identical
- * level == 2: analyze non-identical changes for minimal conflict set
- * level == 3: analyze non-identical changes for minimal conflict set, but
- * treat hunks not containing any letter or number as conflicting
- *
- * returns < 0 on error, == 0 for no conflicts, else number of conflicts
- */
-static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
- xdfenv_t *xe2, xdchange_t *xscr2,
- xmparam_t const *xmp, mmbuffer_t *result)
-{
- xdmerge_t *changes, *c;
- xpparam_t const *xpp = &xmp->xpp;
- const char *const ancestor_name = xmp->ancestor;
- const char *const name1 = xmp->file1;
- const char *const name2 = xmp->file2;
- int i0, i1, i2, chg0, chg1, chg2;
- int level = xmp->level;
- int style = xmp->style;
- int favor = xmp->favor;
-
- if (style == XDL_MERGE_DIFF3) {
- /*
- * "diff3 -m" output does not make sense for anything
- * more aggressive than XDL_MERGE_EAGER.
- */
- if (XDL_MERGE_EAGER < level)
- level = XDL_MERGE_EAGER;
- }
-
- c = changes = NULL;
-
- while (xscr1 && xscr2) {
- if (!changes)
- changes = c;
- if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
- i0 = xscr1->i1;
- i1 = xscr1->i2;
- i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
- chg0 = xscr1->chg1;
- chg1 = xscr1->chg2;
- chg2 = xscr1->chg1;
- if (xdl_append_merge(&c, 1,
- i0, chg0, i1, chg1, i2, chg2)) {
- xdl_cleanup_merge(changes);
- return -1;
- }
- xscr1 = xscr1->next;
- continue;
- }
- if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
- i0 = xscr2->i1;
- i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
- i2 = xscr2->i2;
- chg0 = xscr2->chg1;
- chg1 = xscr2->chg1;
- chg2 = xscr2->chg2;
- if (xdl_append_merge(&c, 2,
- i0, chg0, i1, chg1, i2, chg2)) {
- xdl_cleanup_merge(changes);
- return -1;
- }
- xscr2 = xscr2->next;
- continue;
- }
- if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||
- xscr1->chg1 != xscr2->chg1 ||
- xscr1->chg2 != xscr2->chg2 ||
- xdl_merge_cmp_lines(xe1, xscr1->i2,
- xe2, xscr2->i2,
- xscr1->chg2, xpp->flags)) {
- /* conflict */
- int off = xscr1->i1 - xscr2->i1;
- int ffo = off + xscr1->chg1 - xscr2->chg1;
-
- i0 = xscr1->i1;
- i1 = xscr1->i2;
- i2 = xscr2->i2;
- if (off > 0) {
- i0 -= off;
- i1 -= off;
- }
- else
- i2 += off;
- chg0 = xscr1->i1 + xscr1->chg1 - i0;
- chg1 = xscr1->i2 + xscr1->chg2 - i1;
- chg2 = xscr2->i2 + xscr2->chg2 - i2;
- if (ffo < 0) {
- chg0 -= ffo;
- chg1 -= ffo;
- } else
- chg2 += ffo;
- if (xdl_append_merge(&c, 0,
- i0, chg0, i1, chg1, i2, chg2)) {
- xdl_cleanup_merge(changes);
- return -1;
- }
- }
-
- i1 = xscr1->i1 + xscr1->chg1;
- i2 = xscr2->i1 + xscr2->chg1;
-
- if (i1 >= i2)
- xscr2 = xscr2->next;
- if (i2 >= i1)
- xscr1 = xscr1->next;
- }
- while (xscr1) {
- if (!changes)
- changes = c;
- i0 = xscr1->i1;
- i1 = xscr1->i2;
- i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
- chg0 = xscr1->chg1;
- chg1 = xscr1->chg2;
- chg2 = xscr1->chg1;
- if (xdl_append_merge(&c, 1,
- i0, chg0, i1, chg1, i2, chg2)) {
- xdl_cleanup_merge(changes);
- return -1;
- }
- xscr1 = xscr1->next;
- }
- while (xscr2) {
- if (!changes)
- changes = c;
- i0 = xscr2->i1;
- i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
- i2 = xscr2->i2;
- chg0 = xscr2->chg1;
- chg1 = xscr2->chg1;
- chg2 = xscr2->chg2;
- if (xdl_append_merge(&c, 2,
- i0, chg0, i1, chg1, i2, chg2)) {
- xdl_cleanup_merge(changes);
- return -1;
- }
- xscr2 = xscr2->next;
- }
- if (!changes)
- changes = c;
- /* refine conflicts */
- if (XDL_MERGE_ZEALOUS <= level &&
- (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
- xdl_simplify_non_conflicts(xe1, changes,
- XDL_MERGE_ZEALOUS < level) < 0)) {
- xdl_cleanup_merge(changes);
- return -1;
- }
- /* output */
- if (result) {
- int marker_size = xmp->marker_size;
- size_t size;
-
- if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2,
- ancestor_name,
- favor, changes, NULL, style,
- marker_size) < 0)
- return -1;
-
- result->ptr = xdl_malloc(size);
- if (!result->ptr) {
- xdl_cleanup_merge(changes);
- return -1;
- }
- result->size = size;
- if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2,
- ancestor_name, favor, changes,
- result->ptr, style, marker_size) < 0)
- return -1;
- }
- return xdl_cleanup_merge(changes);
-}
-
-int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
- xmparam_t const *xmp, mmbuffer_t *result)
-{
- xdchange_t *xscr1, *xscr2;
- xdfenv_t xe1, xe2;
- int status;
- xpparam_t const *xpp = &xmp->xpp;
-
- result->ptr = NULL;
- result->size = 0;
-
- if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0) {
- return -1;
- }
- if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {
- xdl_free_env(&xe1);
- return -1;
- }
- if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||
- xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 ||
- xdl_build_script(&xe1, &xscr1) < 0) {
- xdl_free_env(&xe1);
- return -1;
- }
- if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 ||
- xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 ||
- xdl_build_script(&xe2, &xscr2) < 0) {
- xdl_free_script(xscr1);
- xdl_free_env(&xe1);
- xdl_free_env(&xe2);
- return -1;
- }
- status = 0;
- if (!xscr1) {
- result->ptr = xdl_malloc(mf2->size);
- memcpy(result->ptr, mf2->ptr, mf2->size);
- result->size = mf2->size;
- } else if (!xscr2) {
- result->ptr = xdl_malloc(mf1->size);
- memcpy(result->ptr, mf1->ptr, mf1->size);
- result->size = mf1->size;
- } else {
- status = xdl_do_merge(&xe1, xscr1,
- &xe2, xscr2,
- xmp, result);
- }
- xdl_free_script(xscr1);
- xdl_free_script(xscr2);
-
- xdl_free_env(&xe1);
- xdl_free_env(&xe2);
-
- return status;
-}
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003-2009 Davide Libenzi, Johannes E. Schindelin
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-#include "xinclude.h"
-#include "xtypes.h"
-#include "xdiff.h"
-
-/*
- * The basic idea of patience diff is to find lines that are unique in
- * both files. These are intuitively the ones that we want to see as
- * common lines.
- *
- * The maximal ordered sequence of such line pairs (where ordered means
- * that the order in the sequence agrees with the order of the lines in
- * both files) naturally defines an initial set of common lines.
- *
- * Now, the algorithm tries to extend the set of common lines by growing
- * the line ranges where the files have identical lines.
- *
- * Between those common lines, the patience diff algorithm is applied
- * recursively, until no unique line pairs can be found; these line ranges
- * are handled by the well-known Myers algorithm.
- */
-
-#define NON_UNIQUE ULONG_MAX
-
-/*
- * This is a hash mapping from line hash to line numbers in the first and
- * second file.
- */
-struct hashmap {
- int nr, alloc;
- struct entry {
- unsigned long hash;
- /*
- * 0 = unused entry, 1 = first line, 2 = second, etc.
- * line2 is NON_UNIQUE if the line is not unique
- * in either the first or the second file.
- */
- unsigned long line1, line2;
- /*
- * "next" & "previous" are used for the longest common
- * sequence;
- * initially, "next" reflects only the order in file1.
- */
- struct entry *next, *previous;
- } *entries, *first, *last;
- /* were common records found? */
- unsigned long has_matches;
- mmfile_t *file1, *file2;
- xdfenv_t *env;
- xpparam_t const *xpp;
-};
-
-/* The argument "pass" is 1 for the first file, 2 for the second. */
-static void insert_record(int line, struct hashmap *map, int pass)
-{
- xrecord_t **records = pass == 1 ?
- map->env->xdf1.recs : map->env->xdf2.recs;
- xrecord_t *record = records[line - 1], *other;
- /*
- * After xdl_prepare_env() (or more precisely, due to
- * xdl_classify_record()), the "ha" member of the records (AKA lines)
- * is _not_ the hash anymore, but a linearized version of it. In
- * other words, the "ha" member is guaranteed to start with 0 and
- * the second record's ha can only be 0 or 1, etc.
- *
- * So we multiply ha by 2 in the hope that the hashing was
- * "unique enough".
- */
- int index = (int)((record->ha << 1) % map->alloc);
-
- while (map->entries[index].line1) {
- other = map->env->xdf1.recs[map->entries[index].line1 - 1];
- if (map->entries[index].hash != record->ha ||
- !xdl_recmatch(record->ptr, record->size,
- other->ptr, other->size,
- map->xpp->flags)) {
- if (++index >= map->alloc)
- index = 0;
- continue;
- }
- if (pass == 2)
- map->has_matches = 1;
- if (pass == 1 || map->entries[index].line2)
- map->entries[index].line2 = NON_UNIQUE;
- else
- map->entries[index].line2 = line;
- return;
- }
- if (pass == 2)
- return;
- map->entries[index].line1 = line;
- map->entries[index].hash = record->ha;
- if (!map->first)
- map->first = map->entries + index;
- if (map->last) {
- map->last->next = map->entries + index;
- map->entries[index].previous = map->last;
- }
- map->last = map->entries + index;
- map->nr++;
-}
-
-/*
- * This function has to be called for each recursion into the inter-hunk
- * parts, as previously non-unique lines can become unique when being
- * restricted to a smaller part of the files.
- *
- * It is assumed that env has been prepared using xdl_prepare().
- */
-static int fill_hashmap(mmfile_t *file1, mmfile_t *file2,
- xpparam_t const *xpp, xdfenv_t *env,
- struct hashmap *result,
- int line1, int count1, int line2, int count2)
-{
- result->file1 = file1;
- result->file2 = file2;
- result->xpp = xpp;
- result->env = env;
-
- /* We know exactly how large we want the hash map */
- result->alloc = count1 * 2;
- result->entries = (struct entry *)
- xdl_malloc(result->alloc * sizeof(struct entry));
- if (!result->entries)
- return -1;
- memset(result->entries, 0, result->alloc * sizeof(struct entry));
-
- /* First, fill with entries from the first file */
- while (count1--)
- insert_record(line1++, result, 1);
-
- /* Then search for matches in the second file */
- while (count2--)
- insert_record(line2++, result, 2);
-
- return 0;
-}
-
-/*
- * Find the longest sequence with a smaller last element (meaning a smaller
- * line2, as we construct the sequence with entries ordered by line1).
- */
-static int binary_search(struct entry **sequence, int longest,
- struct entry *entry)
-{
- int left = -1, right = longest;
-
- while (left + 1 < right) {
- int middle = (left + right) / 2;
- /* by construction, no two entries can be equal */
- if (sequence[middle]->line2 > entry->line2)
- right = middle;
- else
- left = middle;
- }
- /* return the index in "sequence", _not_ the sequence length */
- return left;
-}
-
-/*
- * The idea is to start with the list of common unique lines sorted by
- * the order in file1. For each of these pairs, the longest (partial)
- * sequence whose last element's line2 is smaller is determined.
- *
- * For efficiency, the sequences are kept in a list containing exactly one
- * item per sequence length: the sequence with the smallest last
- * element (in terms of line2).
- */
-static struct entry *find_longest_common_sequence(struct hashmap *map)
-{
- struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *));
- int longest = 0, i;
- struct entry *entry;
-
- for (entry = map->first; entry; entry = entry->next) {
- if (!entry->line2 || entry->line2 == NON_UNIQUE)
- continue;
- i = binary_search(sequence, longest, entry);
- entry->previous = i < 0 ? NULL : sequence[i];
- sequence[++i] = entry;
- if (i == longest)
- longest++;
- }
-
- /* No common unique lines were found */
- if (!longest) {
- xdl_free(sequence);
- return NULL;
- }
-
- /* Iterate starting at the last element, adjusting the "next" members */
- entry = sequence[longest - 1];
- entry->next = NULL;
- while (entry->previous) {
- entry->previous->next = entry;
- entry = entry->previous;
- }
- xdl_free(sequence);
- return entry;
-}
-
-static int match(struct hashmap *map, int line1, int line2)
-{
- xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];
- xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];
- return xdl_recmatch(record1->ptr, record1->size,
- record2->ptr, record2->size, map->xpp->flags);
-}
-
-static int patience_diff(mmfile_t *file1, mmfile_t *file2,
- xpparam_t const *xpp, xdfenv_t *env,
- int line1, int count1, int line2, int count2);
-
-static int walk_common_sequence(struct hashmap *map, struct entry *first,
- int line1, int count1, int line2, int count2)
-{
- int end1 = line1 + count1, end2 = line2 + count2;
- int next1, next2;
-
- for (;;) {
- /* Try to grow the line ranges of common lines */
- if (first) {
- next1 = first->line1;
- next2 = first->line2;
- while (next1 > line1 && next2 > line2 &&
- match(map, next1 - 1, next2 - 1)) {
- next1--;
- next2--;
- }
- } else {
- next1 = end1;
- next2 = end2;
- }
- while (line1 < next1 && line2 < next2 &&
- match(map, line1, line2)) {
- line1++;
- line2++;
- }
-
- /* Recurse */
- if (next1 > line1 || next2 > line2) {
- struct hashmap submap;
-
- memset(&submap, 0, sizeof(submap));
- if (patience_diff(map->file1, map->file2,
- map->xpp, map->env,
- line1, next1 - line1,
- line2, next2 - line2))
- return -1;
- }
-
- if (!first)
- return 0;
-
- while (first->next &&
- first->next->line1 == first->line1 + 1 &&
- first->next->line2 == first->line2 + 1)
- first = first->next;
-
- line1 = first->line1 + 1;
- line2 = first->line2 + 1;
-
- first = first->next;
- }
-}
-
-static int fall_back_to_classic_diff(struct hashmap *map,
- int line1, int count1, int line2, int count2)
-{
- xpparam_t xpp;
- xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
-
- return xdl_fall_back_diff(map->env, &xpp,
- line1, count1, line2, count2);
-}
-
-/*
- * Recursively find the longest common sequence of unique lines,
- * and if none was found, ask xdl_do_diff() to do the job.
- *
- * This function assumes that env was prepared with xdl_prepare_env().
- */
-static int patience_diff(mmfile_t *file1, mmfile_t *file2,
- xpparam_t const *xpp, xdfenv_t *env,
- int line1, int count1, int line2, int count2)
-{
- struct hashmap map;
- struct entry *first;
- int result = 0;
-
- /* trivial case: one side is empty */
- if (!count1) {
- while(count2--)
- env->xdf2.rchg[line2++ - 1] = 1;
- return 0;
- } else if (!count2) {
- while(count1--)
- env->xdf1.rchg[line1++ - 1] = 1;
- return 0;
- }
-
- memset(&map, 0, sizeof(map));
- if (fill_hashmap(file1, file2, xpp, env, &map,
- line1, count1, line2, count2))
- return -1;
-
- /* are there any matching lines at all? */
- if (!map.has_matches) {
- while(count1--)
- env->xdf1.rchg[line1++ - 1] = 1;
- while(count2--)
- env->xdf2.rchg[line2++ - 1] = 1;
- xdl_free(map.entries);
- return 0;
- }
-
- first = find_longest_common_sequence(&map);
- if (first)
- result = walk_common_sequence(&map, first,
- line1, count1, line2, count2);
- else
- result = fall_back_to_classic_diff(&map,
- line1, count1, line2, count2);
-
- xdl_free(map.entries);
- return result;
-}
-
-int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2,
- xpparam_t const *xpp, xdfenv_t *env)
-{
- if (xdl_prepare_env(file1, file2, xpp, env) < 0)
- return -1;
-
- /* environment is cleaned up in xdl_diff() */
- return patience_diff(file1, file2, xpp, env,
- 1, env->xdf1.nrec, 1, env->xdf2.nrec);
-}
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#include "xinclude.h"
-
-
-#define XDL_KPDIS_RUN 4
-#define XDL_MAX_EQLIMIT 1024
-#define XDL_SIMSCAN_WINDOW 100
-#define XDL_GUESS_NLINES1 256
-#define XDL_GUESS_NLINES2 20
-
-
-typedef struct s_xdlclass {
- struct s_xdlclass *next;
- unsigned long ha;
- char const *line;
- long size;
- long idx;
- long len1, len2;
-} xdlclass_t;
-
-typedef struct s_xdlclassifier {
- unsigned int hbits;
- long hsize;
- xdlclass_t **rchash;
- chastore_t ncha;
- xdlclass_t **rcrecs;
- long alloc;
- long count;
- long flags;
-} xdlclassifier_t;
-
-
-
-
-static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);
-static void xdl_free_classifier(xdlclassifier_t *cf);
-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
- unsigned int hbits, xrecord_t *rec);
-static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
- xdlclassifier_t *cf, xdfile_t *xdf);
-static void xdl_free_ctx(xdfile_t *xdf);
-static int xdl_clean_mmatch(char const *dis, long i, long s, long e);
-static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
-static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
-static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
-
-
-
-
-static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
- cf->flags = flags;
-
- cf->hbits = xdl_hashbits((unsigned int) size);
- cf->hsize = 1 << cf->hbits;
-
- if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) {
-
- return -1;
- }
- if (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) {
-
- xdl_cha_free(&cf->ncha);
- return -1;
- }
- memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));
-
- cf->alloc = size;
- if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) {
-
- xdl_free(cf->rchash);
- xdl_cha_free(&cf->ncha);
- return -1;
- }
-
- cf->count = 0;
-
- return 0;
-}
-
-
-static void xdl_free_classifier(xdlclassifier_t *cf) {
-
- xdl_free(cf->rcrecs);
- xdl_free(cf->rchash);
- xdl_cha_free(&cf->ncha);
-}
-
-
-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
- unsigned int hbits, xrecord_t *rec) {
- long hi;
- char const *line;
- xdlclass_t *rcrec;
- xdlclass_t **rcrecs;
-
- line = rec->ptr;
- hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
- for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)
- if (rcrec->ha == rec->ha &&
- xdl_recmatch(rcrec->line, rcrec->size,
- rec->ptr, rec->size, cf->flags))
- break;
-
- if (!rcrec) {
- if (!(rcrec = xdl_cha_alloc(&cf->ncha))) {
-
- return -1;
- }
- rcrec->idx = cf->count++;
- if (cf->count > cf->alloc) {
- cf->alloc *= 2;
- if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) {
-
- return -1;
- }
- cf->rcrecs = rcrecs;
- }
- cf->rcrecs[rcrec->idx] = rcrec;
- rcrec->line = line;
- rcrec->size = rec->size;
- rcrec->ha = rec->ha;
- rcrec->len1 = rcrec->len2 = 0;
- rcrec->next = cf->rchash[hi];
- cf->rchash[hi] = rcrec;
- }
-
- (pass == 1) ? rcrec->len1++ : rcrec->len2++;
-
- rec->ha = (unsigned long) rcrec->idx;
-
- hi = (long) XDL_HASHLONG(rec->ha, hbits);
- rec->next = rhash[hi];
- rhash[hi] = rec;
-
- return 0;
-}
-
-
-static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
- xdlclassifier_t *cf, xdfile_t *xdf) {
- unsigned int hbits;
- long nrec, hsize, bsize;
- unsigned long hav;
- char const *blk, *cur, *top, *prev;
- xrecord_t *crec;
- xrecord_t **recs, **rrecs;
- xrecord_t **rhash;
- unsigned long *ha;
- char *rchg;
- long *rindex;
-
- ha = NULL;
- rindex = NULL;
- rchg = NULL;
- rhash = NULL;
- recs = NULL;
-
- if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
- goto abort;
- if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
- goto abort;
-
- if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
- hbits = hsize = 0;
- else {
- hbits = xdl_hashbits((unsigned int) narec);
- hsize = 1 << hbits;
- if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
- goto abort;
- memset(rhash, 0, hsize * sizeof(xrecord_t *));
- }
-
- nrec = 0;
- if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
- for (top = blk + bsize; cur < top; ) {
- prev = cur;
- hav = xdl_hash_record(&cur, top, xpp->flags);
- if (nrec >= narec) {
- narec *= 2;
- if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
- goto abort;
- recs = rrecs;
- }
- if (!(crec = xdl_cha_alloc(&xdf->rcha)))
- goto abort;
- crec->ptr = prev;
- crec->size = (long) (cur - prev);
- crec->ha = hav;
- recs[nrec++] = crec;
-
- if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
- xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
- goto abort;
- }
- }
-
- if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
- goto abort;
- memset(rchg, 0, (nrec + 2) * sizeof(char));
-
- if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))
- goto abort;
- if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))
- goto abort;
-
- xdf->nrec = nrec;
- xdf->recs = recs;
- xdf->hbits = hbits;
- xdf->rhash = rhash;
- xdf->rchg = rchg + 1;
- xdf->rindex = rindex;
- xdf->nreff = 0;
- xdf->ha = ha;
- xdf->dstart = 0;
- xdf->dend = nrec - 1;
-
- return 0;
-
-abort:
- xdl_free(ha);
- xdl_free(rindex);
- xdl_free(rchg);
- xdl_free(rhash);
- xdl_free(recs);
- xdl_cha_free(&xdf->rcha);
- return -1;
-}
-
-
-static void xdl_free_ctx(xdfile_t *xdf) {
-
- xdl_free(xdf->rhash);
- xdl_free(xdf->rindex);
- xdl_free(xdf->rchg - 1);
- xdl_free(xdf->ha);
- xdl_free(xdf->recs);
- xdl_cha_free(&xdf->rcha);
-}
-
-
-int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
- xdfenv_t *xe) {
- long enl1, enl2, sample;
- xdlclassifier_t cf;
-
- memset(&cf, 0, sizeof(cf));
-
- /*
- * For histogram diff, we can afford a smaller sample size and
- * thus a poorer estimate of the number of lines, as the hash
- * table (rhash) won't be filled up/grown. The number of lines
- * (nrecs) will be updated correctly anyway by
- * xdl_prepare_ctx().
- */
- sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
- ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
-
- enl1 = xdl_guess_lines(mf1, sample) + 1;
- enl2 = xdl_guess_lines(mf2, sample) + 1;
-
- if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF &&
- xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
- return -1;
-
- if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
-
- xdl_free_classifier(&cf);
- return -1;
- }
- if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
-
- xdl_free_ctx(&xe->xdf1);
- xdl_free_classifier(&cf);
- return -1;
- }
-
- if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
- (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
- xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
-
- xdl_free_ctx(&xe->xdf2);
- xdl_free_ctx(&xe->xdf1);
- xdl_free_classifier(&cf);
- return -1;
- }
-
- if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)
- xdl_free_classifier(&cf);
-
- return 0;
-}
-
-
-void xdl_free_env(xdfenv_t *xe) {
-
- xdl_free_ctx(&xe->xdf2);
- xdl_free_ctx(&xe->xdf1);
-}
-
-
-static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
- long r, rdis0, rpdis0, rdis1, rpdis1;
-
- /*
- * Limits the window the is examined during the similar-lines
- * scan. The loops below stops when dis[i - r] == 1 (line that
- * has no match), but there are corner cases where the loop
- * proceed all the way to the extremities by causing huge
- * performance penalties in case of big files.
- */
- if (i - s > XDL_SIMSCAN_WINDOW)
- s = i - XDL_SIMSCAN_WINDOW;
- if (e - i > XDL_SIMSCAN_WINDOW)
- e = i + XDL_SIMSCAN_WINDOW;
-
- /*
- * Scans the lines before 'i' to find a run of lines that either
- * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).
- * Note that we always call this function with dis[i] > 1, so the
- * current line (i) is already a multimatch line.
- */
- for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {
- if (!dis[i - r])
- rdis0++;
- else if (dis[i - r] == 2)
- rpdis0++;
- else
- break;
- }
- /*
- * If the run before the line 'i' found only multimatch lines, we
- * return 0 and hence we don't make the current line (i) discarded.
- * We want to discard multimatch lines only when they appear in the
- * middle of runs with nomatch lines (dis[j] == 0).
- */
- if (rdis0 == 0)
- return 0;
- for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {
- if (!dis[i + r])
- rdis1++;
- else if (dis[i + r] == 2)
- rpdis1++;
- else
- break;
- }
- /*
- * If the run after the line 'i' found only multimatch lines, we
- * return 0 and hence we don't make the current line (i) discarded.
- */
- if (rdis1 == 0)
- return 0;
- rdis1 += rdis0;
- rpdis1 += rpdis0;
-
- return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);
-}
-
-
-/*
- * Try to reduce the problem complexity, discard records that have no
- * matches on the other file. Also, lines that have multiple matches
- * might be potentially discarded if they happear in a run of discardable.
- */
-static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
- long i, nm, nreff, mlim;
- xrecord_t **recs;
- xdlclass_t *rcrec;
- char *dis, *dis1, *dis2;
-
- if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {
-
- return -1;
- }
- memset(dis, 0, xdf1->nrec + xdf2->nrec + 2);
- dis1 = dis;
- dis2 = dis1 + xdf1->nrec + 1;
-
- if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
- mlim = XDL_MAX_EQLIMIT;
- for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
- rcrec = cf->rcrecs[(*recs)->ha];
- nm = rcrec ? rcrec->len2 : 0;
- dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
- }
-
- if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
- mlim = XDL_MAX_EQLIMIT;
- for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
- rcrec = cf->rcrecs[(*recs)->ha];
- nm = rcrec ? rcrec->len1 : 0;
- dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
- }
-
- for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
- i <= xdf1->dend; i++, recs++) {
- if (dis1[i] == 1 ||
- (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {
- xdf1->rindex[nreff] = i;
- xdf1->ha[nreff] = (*recs)->ha;
- nreff++;
- } else
- xdf1->rchg[i] = 1;
- }
- xdf1->nreff = nreff;
-
- for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
- i <= xdf2->dend; i++, recs++) {
- if (dis2[i] == 1 ||
- (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {
- xdf2->rindex[nreff] = i;
- xdf2->ha[nreff] = (*recs)->ha;
- nreff++;
- } else
- xdf2->rchg[i] = 1;
- }
- xdf2->nreff = nreff;
-
- xdl_free(dis);
-
- return 0;
-}
-
-
-/*
- * Early trim initial and terminal matching records.
- */
-static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
- long i, lim;
- xrecord_t **recs1, **recs2;
-
- recs1 = xdf1->recs;
- recs2 = xdf2->recs;
- for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;
- i++, recs1++, recs2++)
- if ((*recs1)->ha != (*recs2)->ha)
- break;
-
- xdf1->dstart = xdf2->dstart = i;
-
- recs1 = xdf1->recs + xdf1->nrec - 1;
- recs2 = xdf2->recs + xdf2->nrec - 1;
- for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
- if ((*recs1)->ha != (*recs2)->ha)
- break;
-
- xdf1->dend = xdf1->nrec - i - 1;
- xdf2->dend = xdf2->nrec - i - 1;
-
- return 0;
-}
-
-
-static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
-
- if (xdl_trim_ends(xdf1, xdf2) < 0 ||
- xdl_cleanup_records(cf, xdf1, xdf2) < 0) {
-
- return -1;
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#if !defined(XPREPARE_H)
-#define XPREPARE_H
-
-
-
-int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
- xdfenv_t *xe);
-void xdl_free_env(xdfenv_t *xe);
-
-
-
-#endif /* #if !defined(XPREPARE_H) */
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#if !defined(XTYPES_H)
-#define XTYPES_H
-
-
-
-typedef struct s_chanode {
- struct s_chanode *next;
- long icurr;
-} chanode_t;
-
-typedef struct s_chastore {
- chanode_t *head, *tail;
- long isize, nsize;
- chanode_t *ancur;
- chanode_t *sncur;
- long scurr;
-} chastore_t;
-
-typedef struct s_xrecord {
- struct s_xrecord *next;
- char const *ptr;
- long size;
- unsigned long ha;
-} xrecord_t;
-
-typedef struct s_xdfile {
- chastore_t rcha;
- long nrec;
- unsigned int hbits;
- xrecord_t **rhash;
- long dstart, dend;
- xrecord_t **recs;
- char *rchg;
- long *rindex;
- long nreff;
- unsigned long *ha;
-} xdfile_t;
-
-typedef struct s_xdfenv {
- xdfile_t xdf1, xdf2;
-} xdfenv_t;
-
-
-
-#endif /* #if !defined(XTYPES_H) */
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#include "xinclude.h"
-
-
-
-
-long xdl_bogosqrt(long n) {
- long i;
-
- /*
- * Classical integer square root approximation using shifts.
- */
- for (i = 1; n > 0; n >>= 2)
- i <<= 1;
-
- return i;
-}
-
-
-int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
- xdemitcb_t *ecb) {
- int i = 2;
- mmbuffer_t mb[3];
-
- mb[0].ptr = (char *) pre;
- mb[0].size = psize;
- mb[1].ptr = (char *) rec;
- mb[1].size = size;
- if (size > 0 && rec[size - 1] != '\n') {
- mb[2].ptr = (char *) "\n\\ No newline at end of file\n";
- mb[2].size = strlen(mb[2].ptr);
- i++;
- }
- if (ecb->outf(ecb->priv, mb, i) < 0) {
-
- return -1;
- }
-
- return 0;
-}
-
-void *xdl_mmfile_first(mmfile_t *mmf, long *size)
-{
- *size = (long)mmf->size;
- return mmf->ptr;
-}
-
-
-long xdl_mmfile_size(mmfile_t *mmf)
-{
- return (long)mmf->size;
-}
-
-
-int xdl_cha_init(chastore_t *cha, long isize, long icount) {
-
- cha->head = cha->tail = NULL;
- cha->isize = isize;
- cha->nsize = icount * isize;
- cha->ancur = cha->sncur = NULL;
- cha->scurr = 0;
-
- return 0;
-}
-
-
-void xdl_cha_free(chastore_t *cha) {
- chanode_t *cur, *tmp;
-
- for (cur = cha->head; (tmp = cur) != NULL;) {
- cur = cur->next;
- xdl_free(tmp);
- }
-}
-
-
-void *xdl_cha_alloc(chastore_t *cha) {
- chanode_t *ancur;
- void *data;
-
- if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) {
- if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) {
-
- return NULL;
- }
- ancur->icurr = 0;
- ancur->next = NULL;
- if (cha->tail)
- cha->tail->next = ancur;
- if (!cha->head)
- cha->head = ancur;
- cha->tail = ancur;
- cha->ancur = ancur;
- }
-
- data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;
- ancur->icurr += cha->isize;
-
- return data;
-}
-
-long xdl_guess_lines(mmfile_t *mf, long sample) {
- long nl = 0, size, tsize = 0;
- char const *data, *cur, *top;
-
- if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
- for (top = data + size; nl < sample && cur < top; ) {
- nl++;
- if (!(cur = memchr(cur, '\n', top - cur)))
- cur = top;
- else
- cur++;
- }
- tsize += (long) (cur - data);
- }
-
- if (nl && tsize)
- nl = xdl_mmfile_size(mf) / (tsize / nl);
-
- return nl + 1;
-}
-
-int xdl_blankline(const char *line, long size, long flags)
-{
- long i;
-
- if (!(flags & XDF_WHITESPACE_FLAGS))
- return (size <= 1);
-
- for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
- ;
-
- return (i == size);
-}
-
-int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
-{
- int i1, i2;
-
- if (s1 == s2 && !memcmp(l1, l2, s1))
- return 1;
- if (!(flags & XDF_WHITESPACE_FLAGS))
- return 0;
-
- i1 = 0;
- i2 = 0;
-
- /*
- * -w matches everything that matches with -b, and -b in turn
- * matches everything that matches with --ignore-space-at-eol.
- *
- * Each flavor of ignoring needs different logic to skip whitespaces
- * while we have both sides to compare.
- */
- if (flags & XDF_IGNORE_WHITESPACE) {
- goto skip_ws;
- while (i1 < s1 && i2 < s2) {
- if (l1[i1++] != l2[i2++])
- return 0;
- skip_ws:
- while (i1 < s1 && XDL_ISSPACE(l1[i1]))
- i1++;
- while (i2 < s2 && XDL_ISSPACE(l2[i2]))
- i2++;
- }
- } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
- while (i1 < s1 && i2 < s2) {
- if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
- /* Skip matching spaces and try again */
- while (i1 < s1 && XDL_ISSPACE(l1[i1]))
- i1++;
- while (i2 < s2 && XDL_ISSPACE(l2[i2]))
- i2++;
- continue;
- }
- if (l1[i1++] != l2[i2++])
- return 0;
- }
- } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
- while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
- ; /* keep going */
- }
-
- /*
- * After running out of one side, the remaining side must have
- * nothing but whitespace for the lines to match. Note that
- * ignore-whitespace-at-eol case may break out of the loop
- * while there still are characters remaining on both lines.
- */
- if (i1 < s1) {
- while (i1 < s1 && XDL_ISSPACE(l1[i1]))
- i1++;
- if (s1 != i1)
- return 0;
- }
- if (i2 < s2) {
- while (i2 < s2 && XDL_ISSPACE(l2[i2]))
- i2++;
- return (s2 == i2);
- }
- return 1;
-}
-
-static unsigned long xdl_hash_record_with_whitespace(char const **data,
- char const *top, long flags) {
- unsigned long ha = 5381;
- char const *ptr = *data;
-
- for (; ptr < top && *ptr != '\n'; ptr++) {
- if (XDL_ISSPACE(*ptr)) {
- const char *ptr2 = ptr;
- int at_eol;
- while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
- && ptr[1] != '\n')
- ptr++;
- at_eol = (top <= ptr + 1 || ptr[1] == '\n');
- if (flags & XDF_IGNORE_WHITESPACE)
- ; /* already handled */
- else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
- && !at_eol) {
- ha += (ha << 5);
- ha ^= (unsigned long) ' ';
- }
- else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
- && !at_eol) {
- while (ptr2 != ptr + 1) {
- ha += (ha << 5);
- ha ^= (unsigned long) *ptr2;
- ptr2++;
- }
- }
- continue;
- }
- ha += (ha << 5);
- ha ^= (unsigned long) *ptr;
- }
- *data = ptr < top ? ptr + 1: ptr;
-
- return ha;
-}
-
-
-unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
- unsigned long ha = 5381;
- char const *ptr = *data;
-
- if (flags & XDF_WHITESPACE_FLAGS)
- return xdl_hash_record_with_whitespace(data, top, flags);
-
- for (; ptr < top && *ptr != '\n'; ptr++) {
- ha += (ha << 5);
- ha ^= (unsigned long) *ptr;
- }
- *data = ptr < top ? ptr + 1: ptr;
-
- return ha;
-}
-
-
-unsigned int xdl_hashbits(unsigned int size) {
- unsigned int val = 1, bits = 0;
-
- for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++);
- return bits ? bits: 1;
-}
-
-
-int xdl_num_out(char *out, long val) {
- char *ptr, *str = out;
- char buf[32];
-
- ptr = buf + sizeof(buf) - 1;
- *ptr = '\0';
- if (val < 0) {
- *--ptr = '-';
- val = -val;
- }
- for (; val && ptr > buf; val /= 10)
- *--ptr = "0123456789"[val % 10];
- if (*ptr)
- for (; *ptr; ptr++, str++)
- *str = *ptr;
- else
- *str++ = '0';
- *str = '\0';
-
- return (int)(str - out);
-}
-
-
-long xdl_atol(char const *str, char const **next) {
- long val, base;
- char const *top;
-
- for (top = str; XDL_ISDIGIT(*top); top++);
- if (next)
- *next = top;
- for (val = 0, base = 1, top--; top >= str; top--, base *= 10)
- val += base * (long)(*top - '0');
- return val;
-}
-
-
-int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
- const char *func, long funclen, xdemitcb_t *ecb) {
- int nb = 0;
- mmbuffer_t mb;
- char buf[128];
-
- memcpy(buf, "@@ -", 4);
- nb += 4;
-
- nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1);
-
- if (c1 != 1) {
- memcpy(buf + nb, ",", 1);
- nb += 1;
-
- nb += xdl_num_out(buf + nb, c1);
- }
-
- memcpy(buf + nb, " +", 2);
- nb += 2;
-
- nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1);
-
- if (c2 != 1) {
- memcpy(buf + nb, ",", 1);
- nb += 1;
-
- nb += xdl_num_out(buf + nb, c2);
- }
-
- memcpy(buf + nb, " @@", 3);
- nb += 3;
- if (func && funclen) {
- buf[nb++] = ' ';
- if (funclen > (long)sizeof(buf) - nb - 1)
- funclen = (long)sizeof(buf) - nb - 1;
- memcpy(buf + nb, func, funclen);
- nb += funclen;
- }
- buf[nb++] = '\n';
-
- mb.ptr = buf;
- mb.size = nb;
- if (ecb->outf(ecb->priv, &mb, 1) < 0)
- return -1;
-
- return 0;
-}
-
-int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
- int line1, int count1, int line2, int count2)
-{
- /*
- * This probably does not work outside Git, since
- * we have a very simple mmfile structure.
- *
- * Note: ideally, we would reuse the prepared environment, but
- * the libxdiff interface does not (yet) allow for diffing only
- * ranges of lines instead of the whole files.
- */
- mmfile_t subfile1, subfile2;
- xdfenv_t env;
-
- subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
- subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
- diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
- subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
- subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
- diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
- if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
- return -1;
-
- memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
- memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
-
- xdl_free_env(&env);
-
- return 0;
-}
+++ /dev/null
-/*
- * LibXDiff by Davide Libenzi ( File Differential Library )
- * Copyright (C) 2003 Davide Libenzi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Davide Libenzi <davidel@xmailserver.org>
- *
- */
-
-#if !defined(XUTILS_H)
-#define XUTILS_H
-
-
-
-long xdl_bogosqrt(long n);
-int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
- xdemitcb_t *ecb);
-int xdl_cha_init(chastore_t *cha, long isize, long icount);
-void xdl_cha_free(chastore_t *cha);
-void *xdl_cha_alloc(chastore_t *cha);
-void *xdl_cha_first(chastore_t *cha);
-void *xdl_cha_next(chastore_t *cha);
-long xdl_guess_lines(mmfile_t *mf, long sample);
-int xdl_blankline(const char *line, long size, long flags);
-int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
-unsigned long xdl_hash_record(char const **data, char const *top, long flags);
-unsigned int xdl_hashbits(unsigned int size);
-int xdl_num_out(char *out, long val);
-long xdl_atol(char const *str, char const **next);
-int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
- const char *func, long funclen, xdemitcb_t *ecb);
-int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
- int line1, int count1, int line2, int count2);
-
-
-
-#endif /* #if !defined(XUTILS_H) */
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include <zlib.h>
-
-#include "zstream.h"
-#include "buffer.h"
-
-#define ZSTREAM_BUFFER_SIZE (1024 * 1024)
-#define ZSTREAM_BUFFER_MIN_EXTRA 8
-
-static int zstream_seterr(git_zstream *zs)
-{
- if (zs->zerr == Z_OK || zs->zerr == Z_STREAM_END)
- return 0;
-
- if (zs->zerr == Z_MEM_ERROR)
- giterr_set_oom();
- else if (zs->z.msg)
- giterr_set_str(GITERR_ZLIB, zs->z.msg);
- else
- giterr_set(GITERR_ZLIB, "Unknown compression error");
-
- return -1;
-}
-
-int git_zstream_init(git_zstream *zstream, git_zstream_t type)
-{
- zstream->type = type;
-
- if (zstream->type == GIT_ZSTREAM_INFLATE)
- zstream->zerr = inflateInit(&zstream->z);
- else
- zstream->zerr = deflateInit(&zstream->z, Z_DEFAULT_COMPRESSION);
- return zstream_seterr(zstream);
-}
-
-void git_zstream_free(git_zstream *zstream)
-{
- if (zstream->type == GIT_ZSTREAM_INFLATE)
- inflateEnd(&zstream->z);
- else
- deflateEnd(&zstream->z);
-}
-
-void git_zstream_reset(git_zstream *zstream)
-{
- if (zstream->type == GIT_ZSTREAM_INFLATE)
- inflateReset(&zstream->z);
- else
- deflateReset(&zstream->z);
- zstream->in = NULL;
- zstream->in_len = 0;
- zstream->zerr = Z_STREAM_END;
-}
-
-int git_zstream_set_input(git_zstream *zstream, const void *in, size_t in_len)
-{
- zstream->in = in;
- zstream->in_len = in_len;
- zstream->zerr = Z_OK;
- return 0;
-}
-
-bool git_zstream_done(git_zstream *zstream)
-{
- return (!zstream->in_len && zstream->zerr == Z_STREAM_END);
-}
-
-size_t git_zstream_suggest_output_len(git_zstream *zstream)
-{
- if (zstream->in_len > ZSTREAM_BUFFER_SIZE)
- return ZSTREAM_BUFFER_SIZE;
- else if (zstream->in_len > ZSTREAM_BUFFER_MIN_EXTRA)
- return zstream->in_len;
- else
- return ZSTREAM_BUFFER_MIN_EXTRA;
-}
-
-int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream)
-{
- int zflush = Z_FINISH;
- size_t out_remain = *out_len;
-
- if (zstream->in_len && zstream->zerr == Z_STREAM_END) {
- giterr_set(GITERR_ZLIB, "zlib input had trailing garbage");
- return -1;
- }
-
- while (out_remain > 0 && zstream->zerr != Z_STREAM_END) {
- size_t out_queued, in_queued, out_used, in_used;
-
- /* set up in data */
- zstream->z.next_in = (Bytef *)zstream->in;
- zstream->z.avail_in = (uInt)zstream->in_len;
- if ((size_t)zstream->z.avail_in != zstream->in_len) {
- zstream->z.avail_in = INT_MAX;
- zflush = Z_NO_FLUSH;
- } else {
- zflush = Z_FINISH;
- }
- in_queued = (size_t)zstream->z.avail_in;
-
- /* set up out data */
- zstream->z.next_out = out;
- zstream->z.avail_out = (uInt)out_remain;
- if ((size_t)zstream->z.avail_out != out_remain)
- zstream->z.avail_out = INT_MAX;
- out_queued = (size_t)zstream->z.avail_out;
-
- /* compress next chunk */
- if (zstream->type == GIT_ZSTREAM_INFLATE)
- zstream->zerr = inflate(&zstream->z, zflush);
- else
- zstream->zerr = deflate(&zstream->z, zflush);
-
- if (zstream->zerr == Z_STREAM_ERROR)
- return zstream_seterr(zstream);
-
- out_used = (out_queued - zstream->z.avail_out);
- out_remain -= out_used;
- out = ((char *)out) + out_used;
-
- in_used = (in_queued - zstream->z.avail_in);
- zstream->in_len -= in_used;
- zstream->in += in_used;
- }
-
- /* either we finished the input or we did not flush the data */
- assert(zstream->in_len > 0 || zflush == Z_FINISH);
-
- /* set out_size to number of bytes actually written to output */
- *out_len = *out_len - out_remain;
-
- return 0;
-}
-
-static int zstream_buf(git_buf *out, const void *in, size_t in_len, git_zstream_t type)
-{
- git_zstream zs = GIT_ZSTREAM_INIT;
- int error = 0;
-
- if ((error = git_zstream_init(&zs, type)) < 0)
- return error;
-
- if ((error = git_zstream_set_input(&zs, in, in_len)) < 0)
- goto done;
-
- while (!git_zstream_done(&zs)) {
- size_t step = git_zstream_suggest_output_len(&zs), written;
-
- if ((error = git_buf_grow_by(out, step)) < 0)
- goto done;
-
- written = out->asize - out->size;
-
- if ((error = git_zstream_get_output(
- out->ptr + out->size, &written, &zs)) < 0)
- goto done;
-
- out->size += written;
- }
-
- /* NULL terminate for consistency if possible */
- if (out->size < out->asize)
- out->ptr[out->size] = '\0';
-
-done:
- git_zstream_free(&zs);
- return error;
-}
-
-int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len)
-{
- return zstream_buf(out, in, in_len, GIT_ZSTREAM_DEFLATE);
-}
-
-int git_zstream_inflatebuf(git_buf *out, const void *in, size_t in_len)
-{
- return zstream_buf(out, in, in_len, GIT_ZSTREAM_INFLATE);
-}
+++ /dev/null
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_zstream_h__
-#define INCLUDE_zstream_h__
-
-#include <zlib.h>
-
-#include "common.h"
-#include "buffer.h"
-
-typedef enum {
- GIT_ZSTREAM_INFLATE,
- GIT_ZSTREAM_DEFLATE,
-} git_zstream_t;
-
-typedef struct {
- z_stream z;
- git_zstream_t type;
- const char *in;
- size_t in_len;
- int zerr;
-} git_zstream;
-
-#define GIT_ZSTREAM_INIT {{0}}
-
-int git_zstream_init(git_zstream *zstream, git_zstream_t type);
-void git_zstream_free(git_zstream *zstream);
-
-int git_zstream_set_input(git_zstream *zstream, const void *in, size_t in_len);
-
-size_t git_zstream_suggest_output_len(git_zstream *zstream);
-
-int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream);
-
-bool git_zstream_done(git_zstream *zstream);
-
-void git_zstream_reset(git_zstream *zstream);
-
-int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len);
-int git_zstream_inflatebuf(git_buf *out, const void *in, size_t in_len);
-
-#endif /* INCLUDE_zstream_h__ */
clean-cargo-deps.patch
local-jquery.patch
-# TODO: re-enable after the freeze
-#use-system-libgit2.patch
+use-system-libgit2.patch
disable-net-tests.patch
--build=$(DEB_BUILD_RUST_TYPE) \
--host=$(DEB_HOST_RUST_TYPE) \
--target=$(DEB_TARGET_RUST_TYPE)
-# TODO: rm after the freeze
- cp -a debian/libgit2 vendor/libgit2-sys-0.6.6/
override_dh_auto_build-arch:
RUST_BACKTRACE=1 $(MAKE)
$(CURDIR)/config.stamp \
$(CURDIR)/Makefile \
$(CURDIR)/cargo-stage0
-# TODO: rm after the freeze
- -$(RM) -rf vendor/libgit2-sys-0.6.6/libgit2
CROSS_SBUILD = sbuild --profiles=nocheck \
--build-failed-commands '%SBUILD_SHELL' \